Implement status code error errors.

This commit is contained in:
Kavin 2022-10-30 21:40:31 +00:00
parent cb6b49116d
commit 4a8fb80411
No known key found for this signature in database
GPG Key ID: 49451E4482CC5BCD
22 changed files with 236 additions and 87 deletions

View File

@ -19,8 +19,8 @@ import me.kavin.piped.server.handlers.auth.FeedHandlers;
import me.kavin.piped.server.handlers.auth.UserHandlers; import me.kavin.piped.server.handlers.auth.UserHandlers;
import me.kavin.piped.utils.*; import me.kavin.piped.utils.*;
import me.kavin.piped.utils.resp.DeleteUserRequest; import me.kavin.piped.utils.resp.DeleteUserRequest;
import me.kavin.piped.utils.resp.ErrorResponse;
import me.kavin.piped.utils.resp.LoginRequest; import me.kavin.piped.utils.resp.LoginRequest;
import me.kavin.piped.utils.resp.StackTraceResponse;
import me.kavin.piped.utils.resp.SubscriptionUpdateRequest; import me.kavin.piped.utils.resp.SubscriptionUpdateRequest;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
@ -455,9 +455,13 @@ public class ServerLauncher extends MultithreadedHttpServerLauncher {
e = ExceptionHandler.handle(e, path); e = ExceptionHandler.handle(e, path);
if (e instanceof ErrorResponse error) {
return getJsonResponse(error.getCode(), error.getContent(), "private");
}
try { try {
return getJsonResponse(500, Constants.mapper return getJsonResponse(500, Constants.mapper
.writeValueAsBytes(new ErrorResponse(ExceptionUtils.getStackTrace(e), e.getMessage())), "private"); .writeValueAsBytes(new StackTraceResponse(ExceptionUtils.getStackTrace(e), e.getMessage())), "private");
} catch (JsonProcessingException ex) { } catch (JsonProcessingException ex) {
return HttpResponse.ofCode(500); return HttpResponse.ofCode(500);
} }

View File

@ -139,10 +139,13 @@ public class ChannelHandlers {
throws IOException, ExtractionException { throws IOException, ExtractionException {
if (StringUtils.isEmpty(prevpageStr)) if (StringUtils.isEmpty(prevpageStr))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter"));
Page prevpage = mapper.readValue(prevpageStr, Page.class); Page prevpage = mapper.readValue(prevpageStr, Page.class);
if (prevpage == null)
ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter"));
ListExtractor.InfoItemsPage<StreamInfoItem> info = ChannelInfo.getMoreItems(YOUTUBE_SERVICE, ListExtractor.InfoItemsPage<StreamInfoItem> info = ChannelInfo.getMoreItems(YOUTUBE_SERVICE,
"https://youtube.com/channel/" + channelId, prevpage); "https://youtube.com/channel/" + channelId, prevpage);
@ -164,7 +167,7 @@ public class ChannelHandlers {
throws IOException, ExtractionException { throws IOException, ExtractionException {
if (StringUtils.isEmpty(data)) if (StringUtils.isEmpty(data))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("data is a required parameter"));
YouTubeChannelTabHandler tabHandler = mapper.readValue(data, YouTubeChannelTabHandlerMixin.class); YouTubeChannelTabHandler tabHandler = mapper.readValue(data, YouTubeChannelTabHandlerMixin.class);
@ -184,12 +187,15 @@ public class ChannelHandlers {
public static byte[] channelTabPageResponse(String data, String prevPageStr) throws Exception { public static byte[] channelTabPageResponse(String data, String prevPageStr) throws Exception {
if (StringUtils.isEmpty(data)) if (StringUtils.isEmpty(data))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("data is a required parameter"));
YouTubeChannelTabHandler tabHandler = mapper.readValue(data, YouTubeChannelTabHandlerMixin.class); YouTubeChannelTabHandler tabHandler = mapper.readValue(data, YouTubeChannelTabHandlerMixin.class);
Page prevPage = mapper.readValue(prevPageStr, Page.class); Page prevPage = mapper.readValue(prevPageStr, Page.class);
if (prevPage == null)
ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter"));
var info = ChannelTabInfo.getMoreItems(YOUTUBE_SERVICE, tabHandler, prevPage); var info = ChannelTabInfo.getMoreItems(YOUTUBE_SERVICE, tabHandler, prevPage);
String nextpage = null; String nextpage = null;

View File

@ -9,6 +9,7 @@ import com.rometools.rome.io.SyndFeedOutput;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.kavin.piped.consts.Constants; import me.kavin.piped.consts.Constants;
import me.kavin.piped.server.handlers.auth.AuthPlaylistHandlers; import me.kavin.piped.server.handlers.auth.AuthPlaylistHandlers;
import me.kavin.piped.utils.ExceptionHandler;
import me.kavin.piped.utils.obj.ContentItem; import me.kavin.piped.utils.obj.ContentItem;
import me.kavin.piped.utils.obj.Playlist; import me.kavin.piped.utils.obj.Playlist;
import me.kavin.piped.utils.obj.StreamsPage; import me.kavin.piped.utils.obj.StreamsPage;
@ -35,7 +36,7 @@ public class PlaylistHandlers {
public static byte[] playlistResponse(String playlistId) throws ExtractionException, IOException { public static byte[] playlistResponse(String playlistId) throws ExtractionException, IOException {
if (StringUtils.isBlank(playlistId)) if (StringUtils.isBlank(playlistId))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is a required parameter"));
if (playlistId.matches("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}")) if (playlistId.matches("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"))
return AuthPlaylistHandlers.playlistPipedResponse(playlistId); return AuthPlaylistHandlers.playlistPipedResponse(playlistId);
@ -70,10 +71,13 @@ public class PlaylistHandlers {
throws IOException, ExtractionException { throws IOException, ExtractionException {
if (StringUtils.isEmpty(prevpageStr)) if (StringUtils.isEmpty(prevpageStr))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter"));
Page prevpage = mapper.readValue(prevpageStr, Page.class); Page prevpage = mapper.readValue(prevpageStr, Page.class);
if (prevpage == null)
ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter"));
ListExtractor.InfoItemsPage<StreamInfoItem> info = PlaylistInfo.getMoreItems(YOUTUBE_SERVICE, ListExtractor.InfoItemsPage<StreamInfoItem> info = PlaylistInfo.getMoreItems(YOUTUBE_SERVICE,
"https://www.youtube.com/playlist?list=" + playlistId, prevpage); "https://www.youtube.com/playlist?list=" + playlistId, prevpage);
@ -94,7 +98,7 @@ public class PlaylistHandlers {
public static byte[] playlistRSSResponse(String playlistId) throws ExtractionException, IOException, FeedException { public static byte[] playlistRSSResponse(String playlistId) throws ExtractionException, IOException, FeedException {
if (StringUtils.isBlank(playlistId)) if (StringUtils.isBlank(playlistId))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is a required parameter"));
if (playlistId.matches("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}")) if (playlistId.matches("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"))
return AuthPlaylistHandlers.playlistPipedRSSResponse(playlistId); return AuthPlaylistHandlers.playlistPipedRSSResponse(playlistId);

View File

@ -1,5 +1,6 @@
package me.kavin.piped.server.handlers; package me.kavin.piped.server.handlers;
import me.kavin.piped.utils.ExceptionHandler;
import me.kavin.piped.utils.obj.ContentItem; import me.kavin.piped.utils.obj.ContentItem;
import me.kavin.piped.utils.obj.SearchResults; import me.kavin.piped.utils.obj.SearchResults;
import me.kavin.piped.utils.resp.InvalidRequestResponse; import me.kavin.piped.utils.resp.InvalidRequestResponse;
@ -24,7 +25,7 @@ public class SearchHandlers {
throws IOException, ExtractionException { throws IOException, ExtractionException {
if (StringUtils.isEmpty(query)) if (StringUtils.isEmpty(query))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("query is a required parameter"));
return mapper.writeValueAsBytes(YOUTUBE_SERVICE.getSuggestionExtractor().suggestionList(query)); return mapper.writeValueAsBytes(YOUTUBE_SERVICE.getSuggestionExtractor().suggestionList(query));
@ -34,7 +35,7 @@ public class SearchHandlers {
throws IOException, ExtractionException { throws IOException, ExtractionException {
if (StringUtils.isEmpty(query)) if (StringUtils.isEmpty(query))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("query is a required parameter"));
return mapper.writeValueAsBytes(Arrays.asList( return mapper.writeValueAsBytes(Arrays.asList(
query, query,
@ -62,10 +63,13 @@ public class SearchHandlers {
throws IOException, ExtractionException { throws IOException, ExtractionException {
if (StringUtils.isEmpty(prevpageStr)) if (StringUtils.isEmpty(prevpageStr))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter"));
Page prevpage = mapper.readValue(prevpageStr, Page.class); Page prevpage = mapper.readValue(prevpageStr, Page.class);
if (prevpage == null)
ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter"));
ListExtractor.InfoItemsPage<InfoItem> pages = SearchInfo.getMoreItems(YOUTUBE_SERVICE, ListExtractor.InfoItemsPage<InfoItem> pages = SearchInfo.getMoreItems(YOUTUBE_SERVICE,
YOUTUBE_SERVICE.getSearchQHFactory().fromQuery(q, Collections.singletonList(filter), null), prevpage); YOUTUBE_SERVICE.getSearchQHFactory().fromQuery(q, Collections.singletonList(filter), null), prevpage);

View File

@ -212,7 +212,7 @@ public class StreamHandlers {
public static byte[] commentsPageResponse(String videoId, String prevpageStr) throws Exception { public static byte[] commentsPageResponse(String videoId, String prevpageStr) throws Exception {
if (StringUtils.isEmpty(prevpageStr)) if (StringUtils.isEmpty(prevpageStr))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("nextpage is a required parameter"));
Page prevpage = mapper.readValue(prevpageStr, Page.class); Page prevpage = mapper.readValue(prevpageStr, Page.class);

View File

@ -1,5 +1,6 @@
package me.kavin.piped.server.handlers; package me.kavin.piped.server.handlers;
import me.kavin.piped.utils.ExceptionHandler;
import me.kavin.piped.utils.obj.ContentItem; import me.kavin.piped.utils.obj.ContentItem;
import me.kavin.piped.utils.resp.InvalidRequestResponse; import me.kavin.piped.utils.resp.InvalidRequestResponse;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
@ -20,8 +21,7 @@ public class TrendingHandlers {
throws ExtractionException, IOException { throws ExtractionException, IOException {
if (region == null) if (region == null)
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("region is a required parameter"));
KioskList kioskList = YOUTUBE_SERVICE.getKioskList(); KioskList kioskList = YOUTUBE_SERVICE.getKioskList();
kioskList.forceContentCountry(new ContentCountry(region)); kioskList.forceContentCountry(new ContentCountry(region));

View File

@ -13,6 +13,7 @@ import jakarta.persistence.criteria.JoinType;
import me.kavin.piped.consts.Constants; import me.kavin.piped.consts.Constants;
import me.kavin.piped.utils.DatabaseHelper; import me.kavin.piped.utils.DatabaseHelper;
import me.kavin.piped.utils.DatabaseSessionFactory; import me.kavin.piped.utils.DatabaseSessionFactory;
import me.kavin.piped.utils.ExceptionHandler;
import me.kavin.piped.utils.URLUtils; import me.kavin.piped.utils.URLUtils;
import me.kavin.piped.utils.obj.ContentItem; import me.kavin.piped.utils.obj.ContentItem;
import me.kavin.piped.utils.obj.Playlist; import me.kavin.piped.utils.obj.Playlist;
@ -44,6 +45,10 @@ import static me.kavin.piped.utils.URLUtils.substringYouTube;
public class AuthPlaylistHandlers { public class AuthPlaylistHandlers {
public static byte[] playlistPipedResponse(String playlistId) throws IOException { public static byte[] playlistPipedResponse(String playlistId) throws IOException {
if (StringUtils.isBlank(playlistId))
ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is a required parameter"));
try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) { try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {
var cb = s.getCriteriaBuilder(); var cb = s.getCriteriaBuilder();
var cq = cb.createQuery(me.kavin.piped.utils.obj.db.Playlist.class); var cq = cb.createQuery(me.kavin.piped.utils.obj.db.Playlist.class);
@ -81,6 +86,9 @@ public class AuthPlaylistHandlers {
public static byte[] playlistPipedRSSResponse(String playlistId) public static byte[] playlistPipedRSSResponse(String playlistId)
throws FeedException { throws FeedException {
if (StringUtils.isBlank(playlistId))
ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is required parameter"));
try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) { try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {
var cb = s.getCriteriaBuilder(); var cb = s.getCriteriaBuilder();
var cq = cb.createQuery(me.kavin.piped.utils.obj.db.Playlist.class); var cq = cb.createQuery(me.kavin.piped.utils.obj.db.Playlist.class);
@ -121,12 +129,12 @@ public class AuthPlaylistHandlers {
public static byte[] createPlaylist(String session, String name) throws IOException { public static byte[] createPlaylist(String session, String name) throws IOException {
if (StringUtils.isBlank(session) || StringUtils.isBlank(name)) if (StringUtils.isBlank(session) || StringUtils.isBlank(name))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and name are required parameters"));
User user = DatabaseHelper.getUserFromSession(session); User user = DatabaseHelper.getUserFromSession(session);
if (user == null) if (user == null)
return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse());
try (Session s = DatabaseSessionFactory.createSession()) { try (Session s = DatabaseSessionFactory.createSession()) {
var playlist = new me.kavin.piped.utils.obj.db.Playlist(name, user, "https://i.ytimg.com/"); var playlist = new me.kavin.piped.utils.obj.db.Playlist(name, user, "https://i.ytimg.com/");
@ -145,12 +153,12 @@ public class AuthPlaylistHandlers {
public static byte[] renamePlaylistResponse(String session, String playlistId, String newName) throws IOException { public static byte[] renamePlaylistResponse(String session, String playlistId, String newName) throws IOException {
if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId)) if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and playlistId are required parameters"));
User user = DatabaseHelper.getUserFromSession(session); User user = DatabaseHelper.getUserFromSession(session);
if (user == null) if (user == null)
return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse());
try (Session s = DatabaseSessionFactory.createSession()) { try (Session s = DatabaseSessionFactory.createSession()) {
var playlist = DatabaseHelper.getPlaylistFromId(s, playlistId); var playlist = DatabaseHelper.getPlaylistFromId(s, playlistId);
@ -177,12 +185,12 @@ public class AuthPlaylistHandlers {
public static byte[] deletePlaylistResponse(String session, String playlistId) throws IOException { public static byte[] deletePlaylistResponse(String session, String playlistId) throws IOException {
if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId)) if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and playlistId are required parameters"));
User user = DatabaseHelper.getUserFromSession(session); User user = DatabaseHelper.getUserFromSession(session);
if (user == null) if (user == null)
return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse());
try (Session s = DatabaseSessionFactory.createSession()) { try (Session s = DatabaseSessionFactory.createSession()) {
var playlist = DatabaseHelper.getPlaylistFromId(s, playlistId); var playlist = DatabaseHelper.getPlaylistFromId(s, playlistId);
@ -207,12 +215,12 @@ public class AuthPlaylistHandlers {
public static byte[] addToPlaylistResponse(String session, String playlistId, String videoId) throws IOException, ExtractionException { public static byte[] addToPlaylistResponse(String session, String playlistId, String videoId) throws IOException, ExtractionException {
if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId) || StringUtils.isBlank(videoId)) if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId) || StringUtils.isBlank(videoId))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session, playlistId and videoId are required parameters"));
var user = DatabaseHelper.getUserFromSession(session); var user = DatabaseHelper.getUserFromSession(session);
if (user == null) if (user == null)
return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse());
try (Session s = DatabaseSessionFactory.createSession()) { try (Session s = DatabaseSessionFactory.createSession()) {
var cb = s.getCriteriaBuilder(); var cb = s.getCriteriaBuilder();
@ -268,7 +276,7 @@ public class AuthPlaylistHandlers {
public static byte[] removeFromPlaylistResponse(String session, String playlistId, int index) throws IOException { public static byte[] removeFromPlaylistResponse(String session, String playlistId, int index) throws IOException {
if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId)) if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and playlistId are required parameters"));
try (Session s = DatabaseSessionFactory.createSession()) { try (Session s = DatabaseSessionFactory.createSession()) {
var cb = s.getCriteriaBuilder(); var cb = s.getCriteriaBuilder();
@ -304,12 +312,12 @@ public class AuthPlaylistHandlers {
public static byte[] importPlaylistResponse(String session, String playlistId) throws IOException, ExtractionException { public static byte[] importPlaylistResponse(String session, String playlistId) throws IOException, ExtractionException {
if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId)) if (StringUtils.isBlank(session) || StringUtils.isBlank(playlistId))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and playlistId are required parameters"));
var user = DatabaseHelper.getUserFromSession(session); var user = DatabaseHelper.getUserFromSession(session);
if (user == null) if (user == null)
return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse());
final String url = "https://www.youtube.com/playlist?list=" + playlistId; final String url = "https://www.youtube.com/playlist?list=" + playlistId;
@ -380,14 +388,14 @@ public class AuthPlaylistHandlers {
public static byte[] playlistsResponse(String session) throws IOException { public static byte[] playlistsResponse(String session) throws IOException {
if (StringUtils.isBlank(session)) if (StringUtils.isBlank(session))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session is a required parameter"));
try (Session s = DatabaseSessionFactory.createSession()) { try (Session s = DatabaseSessionFactory.createSession()) {
User user = DatabaseHelper.getUserFromSession(session, s); User user = DatabaseHelper.getUserFromSession(session, s);
if (user == null) if (user == null)
return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse());
var playlists = new ObjectArrayList<>(); var playlists = new ObjectArrayList<>();

View File

@ -39,7 +39,7 @@ public class FeedHandlers {
throws IOException { throws IOException {
if (StringUtils.isBlank(session) || StringUtils.isBlank(channelId)) if (StringUtils.isBlank(session) || StringUtils.isBlank(channelId))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and channelId are required parameters"));
try (Session s = DatabaseSessionFactory.createSession()) { try (Session s = DatabaseSessionFactory.createSession()) {
@ -66,7 +66,8 @@ public class FeedHandlers {
} }
return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse());
return null;
} }
} }
@ -74,7 +75,7 @@ public class FeedHandlers {
public static byte[] isSubscribedResponse(String session, String channelId) throws IOException { public static byte[] isSubscribedResponse(String session, String channelId) throws IOException {
if (StringUtils.isBlank(session) || StringUtils.isBlank(channelId)) if (StringUtils.isBlank(session) || StringUtils.isBlank(channelId))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and channelId are required parameters"));
try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) { try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {
var cb = s.getCriteriaBuilder(); var cb = s.getCriteriaBuilder();
@ -94,7 +95,7 @@ public class FeedHandlers {
public static byte[] feedResponse(String session) throws IOException { public static byte[] feedResponse(String session) throws IOException {
if (StringUtils.isBlank(session)) if (StringUtils.isBlank(session))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session is a required parameter"));
User user = DatabaseHelper.getUserFromSession(session); User user = DatabaseHelper.getUserFromSession(session);
@ -133,13 +134,14 @@ public class FeedHandlers {
} }
} }
return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse());
return null;
} }
public static byte[] feedResponseRSS(String session) throws IOException, FeedException { public static byte[] feedResponseRSS(String session) throws FeedException {
if (StringUtils.isBlank(session)) if (StringUtils.isBlank(session))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session is a required parameter"));
User user = DatabaseHelper.getUserFromSession(session); User user = DatabaseHelper.getUserFromSession(session);
@ -201,7 +203,8 @@ public class FeedHandlers {
} }
} }
return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse());
return null;
} }
public static byte[] unauthenticatedFeedResponse(String[] channelIds) throws Exception { public static byte[] unauthenticatedFeedResponse(String[] channelIds) throws Exception {
@ -255,8 +258,7 @@ public class FeedHandlers {
.collect(Collectors.toUnmodifiableSet()); .collect(Collectors.toUnmodifiableSet());
if (filtered.isEmpty()) if (filtered.isEmpty())
return mapper.writeValueAsBytes(mapper.createObjectNode() ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("No valid channel IDs provided"));
.put("error", "No valid channel IDs provided"));
try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) { try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {
@ -378,7 +380,7 @@ public class FeedHandlers {
public static byte[] importResponse(String session, String[] channelIds, boolean override) throws IOException { public static byte[] importResponse(String session, String[] channelIds, boolean override) throws IOException {
if (StringUtils.isBlank(session)) if (StringUtils.isBlank(session))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session is a required parameter"));
User user = DatabaseHelper.getUserFromSessionWithSubscribed(session); User user = DatabaseHelper.getUserFromSessionWithSubscribed(session);
@ -422,14 +424,15 @@ public class FeedHandlers {
return mapper.writeValueAsBytes(new AcceptedResponse()); return mapper.writeValueAsBytes(new AcceptedResponse());
} }
return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse());
return null;
} }
public static byte[] subscriptionsResponse(String session) public static byte[] subscriptionsResponse(String session)
throws IOException { throws IOException {
if (StringUtils.isBlank(session)) if (StringUtils.isBlank(session))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session is a required parameter"));
User user = DatabaseHelper.getUserFromSession(session); User user = DatabaseHelper.getUserFromSession(session);
@ -461,7 +464,8 @@ public class FeedHandlers {
} }
} }
return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse());
return null;
} }
@ -473,6 +477,9 @@ public class FeedHandlers {
.filter(id -> id.matches("[A-Za-z\\d_-]+")) .filter(id -> id.matches("[A-Za-z\\d_-]+"))
.collect(Collectors.toUnmodifiableSet()); .collect(Collectors.toUnmodifiableSet());
if (filtered.isEmpty())
return mapper.writeValueAsBytes(Collections.EMPTY_LIST);
try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) { try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {
CriteriaBuilder cb = s.getCriteriaBuilder(); CriteriaBuilder cb = s.getCriteriaBuilder();
@ -498,7 +505,7 @@ public class FeedHandlers {
throws IOException { throws IOException {
if (StringUtils.isBlank(session) || StringUtils.isBlank(channelId)) if (StringUtils.isBlank(session) || StringUtils.isBlank(channelId))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and channelId are required parameters"));
User user = DatabaseHelper.getUserFromSession(session); User user = DatabaseHelper.getUserFromSession(session);
@ -513,7 +520,8 @@ public class FeedHandlers {
} }
return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse());
return null;
} }
} }

View File

@ -7,12 +7,12 @@ import jakarta.persistence.criteria.Root;
import me.kavin.piped.consts.Constants; import me.kavin.piped.consts.Constants;
import me.kavin.piped.utils.DatabaseHelper; import me.kavin.piped.utils.DatabaseHelper;
import me.kavin.piped.utils.DatabaseSessionFactory; import me.kavin.piped.utils.DatabaseSessionFactory;
import me.kavin.piped.utils.ExceptionHandler;
import me.kavin.piped.utils.RequestUtils; import me.kavin.piped.utils.RequestUtils;
import me.kavin.piped.utils.obj.db.User; import me.kavin.piped.utils.obj.db.User;
import me.kavin.piped.utils.resp.*; import me.kavin.piped.utils.resp.*;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.StatelessSession; import org.hibernate.StatelessSession;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder; import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
@ -31,16 +31,13 @@ public class UserHandlers {
public static byte[] registerResponse(String user, String pass) throws IOException { public static byte[] registerResponse(String user, String pass) throws IOException {
if (Constants.DISABLE_REGISTRATION) if (Constants.DISABLE_REGISTRATION)
return mapper.writeValueAsBytes(new DisabledRegistrationResponse()); ExceptionHandler.throwErrorResponse(new DisabledRegistrationResponse());
if (StringUtils.isBlank(user) || StringUtils.isBlank(pass)) if (StringUtils.isBlank(user) || StringUtils.isBlank(pass))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse());
if (user.length() > 24) if (user.length() > 24)
return mapper.writeValueAsBytes( ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("The username must be less than 24 characters"));
mapper.createObjectNode()
.put("error", "The username must be less than 24 characters")
);
user = user.toLowerCase(); user = user.toLowerCase();
@ -52,7 +49,7 @@ public class UserHandlers {
boolean registered = s.createQuery(cr).uniqueResult() != null; boolean registered = s.createQuery(cr).uniqueResult() != null;
if (registered) if (registered)
return mapper.writeValueAsBytes(new AlreadyRegisteredResponse()); ExceptionHandler.throwErrorResponse(new AlreadyRegisteredResponse());
if (Constants.COMPROMISED_PASSWORD_CHECK) { if (Constants.COMPROMISED_PASSWORD_CHECK) {
String sha1Hash = DigestUtils.sha1Hex(pass).toUpperCase(); String sha1Hash = DigestUtils.sha1Hex(pass).toUpperCase();
@ -63,7 +60,7 @@ public class UserHandlers {
.split("\n"); .split("\n");
for (String entry : entries) for (String entry : entries)
if (StringUtils.substringBefore(entry, ":").equals(suffix)) if (StringUtils.substringBefore(entry, ":").equals(suffix))
return mapper.writeValueAsBytes(new CompromisedPasswordResponse()); ExceptionHandler.throwErrorResponse(new CompromisedPasswordResponse());
} }
User newuser = new User(user, argon2PasswordEncoder.encode(pass), Set.of()); User newuser = new User(user, argon2PasswordEncoder.encode(pass), Set.of());
@ -87,7 +84,7 @@ public class UserHandlers {
throws IOException { throws IOException {
if (user == null || pass == null) if (user == null || pass == null)
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("username and password are required parameters"));
user = user.toLowerCase(); user = user.toLowerCase();
@ -106,33 +103,30 @@ public class UserHandlers {
} }
} }
return mapper.writeValueAsBytes(new IncorrectCredentialsResponse()); ExceptionHandler.throwErrorResponse(new IncorrectCredentialsResponse());
return null;
} }
} }
public static byte[] deleteUserResponse(String session, String pass) throws IOException { public static byte[] deleteUserResponse(String session, String pass) throws IOException {
if (StringUtils.isBlank(session) || StringUtils.isBlank(pass)) if (StringUtils.isBlank(session) || StringUtils.isBlank(pass))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session and password are required parameters"));
try (Session s = DatabaseSessionFactory.createSession()) { try (Session s = DatabaseSessionFactory.createSession()) {
User user = DatabaseHelper.getUserFromSession(session); User user = DatabaseHelper.getUserFromSession(session);
if (user == null) if (user == null)
return mapper.writeValueAsBytes(new AuthenticationFailureResponse()); ExceptionHandler.throwErrorResponse(new AuthenticationFailureResponse());
String hash = user.getPassword(); String hash = user.getPassword();
if (!hashMatch(hash, pass)) if (!hashMatch(hash, pass))
return mapper.writeValueAsBytes(new IncorrectCredentialsResponse()); ExceptionHandler.throwErrorResponse(new IncorrectCredentialsResponse());
try { var tr = s.beginTransaction();
var tr = s.beginTransaction(); s.remove(user);
s.remove(user); tr.commit();
tr.commit();
} catch (Exception e) {
return mapper.writeValueAsBytes(new ErrorResponse(ExceptionUtils.getStackTrace(e), e.getMessage()));
}
return mapper.writeValueAsBytes(new DeleteUserResponse(user.getUsername())); return mapper.writeValueAsBytes(new DeleteUserResponse(user.getUsername()));
} }
@ -141,7 +135,7 @@ public class UserHandlers {
public static byte[] logoutResponse(String session) throws JsonProcessingException { public static byte[] logoutResponse(String session) throws JsonProcessingException {
if (StringUtils.isBlank(session)) if (StringUtils.isBlank(session))
return mapper.writeValueAsBytes(new InvalidRequestResponse()); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("session is a required parameter"));
try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) { try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {
var tr = s.beginTransaction(); var tr = s.beginTransaction();

View File

@ -0,0 +1,40 @@
package me.kavin.piped.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.Serial;
import static me.kavin.piped.consts.Constants.mapper;
public class ErrorResponse extends Exception {
@Serial
private static final long serialVersionUID = 1L;
private final int code;
private final byte[] content;
public ErrorResponse(int code, byte[] content) {
this.code = code;
this.content = content;
}
public ErrorResponse(IStatusCode statusObj) throws JsonProcessingException {
this.code = statusObj.getStatusCode();
this.content = mapper.writeValueAsBytes(statusObj);
}
public ErrorResponse(int code, Object content) throws JsonProcessingException {
this.code = code;
this.content = mapper.writeValueAsBytes(content);
}
public int getCode() {
return code;
}
public byte[] getContent() {
return content;
}
}

View File

@ -1,7 +1,9 @@
package me.kavin.piped.utils; package me.kavin.piped.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.sentry.Sentry; import io.sentry.Sentry;
import me.kavin.piped.consts.Constants; import me.kavin.piped.consts.Constants;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
@ -29,4 +31,20 @@ public class ExceptionHandler {
return e; return e;
} }
public static void throwErrorResponse(IStatusCode statusObj) {
try {
ExceptionUtils.rethrow(new ErrorResponse(statusObj));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public static void throwErrorResponse(int code, Object content) {
try {
ExceptionUtils.rethrow(new ErrorResponse(code, content));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
} }

View File

@ -0,0 +1,10 @@
package me.kavin.piped.utils;
import com.fasterxml.jackson.annotation.JsonIgnore;
public interface IStatusCode {
@JsonIgnore
public int getStatusCode();
}

View File

@ -1,8 +1,12 @@
package me.kavin.piped.utils; package me.kavin.piped.utils;
import com.grack.nanojson.*; import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonWriter;
import me.kavin.piped.consts.Constants; import me.kavin.piped.consts.Constants;
import me.kavin.piped.utils.resp.InvalidRequestResponse; import me.kavin.piped.utils.resp.InvalidRequestResponse;
import me.kavin.piped.utils.resp.SimpleErrorMessage;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.io.IOException; import java.io.IOException;
@ -13,7 +17,7 @@ import java.security.NoSuchAlgorithmException;
public class SponsorBlockUtils { public class SponsorBlockUtils {
public static String getSponsors(String id, String categories) public static String getSponsors(String id, String categories)
throws IOException, NoSuchAlgorithmException, JsonParserException { throws IOException, NoSuchAlgorithmException {
if (StringUtils.isEmpty(categories)) if (StringUtils.isEmpty(categories))
return Constants.mapper.writeValueAsString(new InvalidRequestResponse()); return Constants.mapper.writeValueAsString(new InvalidRequestResponse());
@ -35,8 +39,9 @@ public class SponsorBlockUtils {
} }
} }
return Constants.mapper.writeValueAsString(Constants.mapper.createObjectNode() ExceptionHandler.throwErrorResponse(new SimpleErrorMessage("All SponsorBlock servers are down"));
.put("error", "All SponsorBlock servers are down"));
return null;
} }
private static String toSha256(final String videoId) throws NoSuchAlgorithmException { private static String toSha256(final String videoId) throws NoSuchAlgorithmException {

View File

@ -1,7 +1,13 @@
package me.kavin.piped.utils.resp; package me.kavin.piped.utils.resp;
public class AlreadyRegisteredResponse { import me.kavin.piped.utils.IStatusCode;
public class AlreadyRegisteredResponse implements IStatusCode {
public String error = "The username you have used is already taken."; public String error = "The username you have used is already taken.";
@Override
public int getStatusCode() {
return 400;
}
} }

View File

@ -1,7 +1,13 @@
package me.kavin.piped.utils.resp; package me.kavin.piped.utils.resp;
public class AuthenticationFailureResponse { import me.kavin.piped.utils.IStatusCode;
public class AuthenticationFailureResponse implements IStatusCode {
public String error = "An invalid Session ID was provided."; public String error = "An invalid Session ID was provided.";
@Override
public int getStatusCode() {
return 401;
}
} }

View File

@ -1,7 +1,13 @@
package me.kavin.piped.utils.resp; package me.kavin.piped.utils.resp;
public class CompromisedPasswordResponse { import me.kavin.piped.utils.IStatusCode;
public class CompromisedPasswordResponse implements IStatusCode {
public String error = "The password you have entered has already been compromised."; public String error = "The password you have entered has already been compromised.";
@Override
public int getStatusCode() {
return 400;
}
} }

View File

@ -1,7 +1,13 @@
package me.kavin.piped.utils.resp; package me.kavin.piped.utils.resp;
public class DisabledRegistrationResponse { import me.kavin.piped.utils.IStatusCode;
public class DisabledRegistrationResponse implements IStatusCode {
public String error = "This instance has registrations disabled."; public String error = "This instance has registrations disabled.";
@Override
public int getStatusCode() {
return 400;
}
} }

View File

@ -1,11 +0,0 @@
package me.kavin.piped.utils.resp;
public class ErrorResponse {
public final String error, message;
public ErrorResponse(String error, String message) {
this.error = error;
this.message = message;
}
}

View File

@ -1,7 +1,13 @@
package me.kavin.piped.utils.resp; package me.kavin.piped.utils.resp;
public class IncorrectCredentialsResponse { import me.kavin.piped.utils.IStatusCode;
public class IncorrectCredentialsResponse implements IStatusCode {
public String error = "The username or password you have entered is incorrect."; public String error = "The username or password you have entered is incorrect.";
@Override
public int getStatusCode() {
return 401;
}
} }

View File

@ -1,7 +1,21 @@
package me.kavin.piped.utils.resp; package me.kavin.piped.utils.resp;
public class InvalidRequestResponse { import me.kavin.piped.utils.IStatusCode;
public String error = "Invalid request sent."; public class InvalidRequestResponse implements IStatusCode {
public String error;
public InvalidRequestResponse(String error) {
this.error = error;
}
public InvalidRequestResponse() {
this.error = "Invalid request sent.";
}
@Override
public int getStatusCode() {
return 400;
}
} }

View File

@ -0,0 +1,10 @@
package me.kavin.piped.utils.resp;
import me.kavin.piped.utils.IStatusCode;
public record SimpleErrorMessage(String error) implements IStatusCode {
@Override
public int getStatusCode() {
return 500;
}
}

View File

@ -0,0 +1,5 @@
package me.kavin.piped.utils.resp;
public record StackTraceResponse(String error, String message) {
}