Commit stash.

This commit is contained in:
FireMasterK 2021-06-09 21:16:56 +05:30
parent da395757d7
commit 55eb4f46fa
No known key found for this signature in database
GPG key ID: 49451E4482CC5BCD
18 changed files with 300 additions and 57 deletions

View file

@ -33,6 +33,7 @@ dependencies {
implementation 'io.activej:activej-launchers-http:4.3' implementation 'io.activej:activej-launchers-http:4.3'
implementation 'org.postgresql:postgresql:42.2.19' implementation 'org.postgresql:postgresql:42.2.19'
implementation 'org.hibernate:hibernate-core:5.4.30.Final' implementation 'org.hibernate:hibernate-core:5.4.30.Final'
implementation 'org.hibernate:hibernate-hikaricp:5.4.30.Final'
implementation 'net.java.dev.jna:jna-platform:5.8.0' implementation 'net.java.dev.jna:jna-platform:5.8.0'
} }

View file

@ -4,6 +4,7 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.localization.Localization; import org.schabi.newpipe.extractor.localization.Localization;
import io.activej.inject.Injector; import io.activej.inject.Injector;
import me.kavin.piped.utils.DatabaseSessionFactory;
import me.kavin.piped.utils.DownloaderImpl; import me.kavin.piped.utils.DownloaderImpl;
public class Main { public class Main {
@ -14,6 +15,10 @@ public class Main {
Injector.useSpecializer(); Injector.useSpecializer();
new Thread(() -> {
DatabaseSessionFactory.createSession().close();
}).start();
new ServerLauncher().launch(args); new ServerLauncher().launch(args);
} }

View file

@ -1,6 +1,7 @@
package me.kavin.piped; package me.kavin.piped;
import static io.activej.config.converter.ConfigConverters.ofInetSocketAddress; import static io.activej.config.converter.ConfigConverters.ofInetSocketAddress;
import static io.activej.http.HttpHeaders.AUTHORIZATION;
import static io.activej.http.HttpHeaders.CACHE_CONTROL; import static io.activej.http.HttpHeaders.CACHE_CONTROL;
import static io.activej.http.HttpHeaders.CONTENT_TYPE; import static io.activej.http.HttpHeaders.CONTENT_TYPE;
@ -35,6 +36,8 @@ import me.kavin.piped.utils.CustomServletDecorator;
import me.kavin.piped.utils.ResponseHelper; import me.kavin.piped.utils.ResponseHelper;
import me.kavin.piped.utils.SponsorBlockUtils; import me.kavin.piped.utils.SponsorBlockUtils;
import me.kavin.piped.utils.resp.ErrorResponse; import me.kavin.piped.utils.resp.ErrorResponse;
import me.kavin.piped.utils.resp.LoginRequest;
import me.kavin.piped.utils.resp.SubscriptionUpdateRequest;
public class ServerLauncher extends MultithreadedHttpServerLauncher { public class ServerLauncher extends MultithreadedHttpServerLauncher {
@ -173,6 +176,41 @@ public class ServerLauncher extends MultithreadedHttpServerLauncher {
} catch (Exception e) { } catch (Exception e) {
return getErrorResponse(e); return getErrorResponse(e);
} }
})).map("/register", AsyncServlet.ofBlocking(executor, request -> {
try {
LoginRequest body = Constants.mapper.readValue(request.loadBody().getResult().asArray(),
LoginRequest.class);
return getJsonResponse(ResponseHelper.registerResponse(body.username, body.password), "private");
} catch (Exception e) {
return getErrorResponse(e);
}
})).map("/login", AsyncServlet.ofBlocking(executor, request -> {
try {
LoginRequest body = Constants.mapper.readValue(request.loadBody().getResult().asArray(),
LoginRequest.class);
return getJsonResponse(ResponseHelper.loginResponse(body.username, body.password), "private");
} catch (Exception e) {
return getErrorResponse(e);
}
})).map("/subscribe", AsyncServlet.ofBlocking(executor, request -> {
try {
SubscriptionUpdateRequest body = Constants.mapper.readValue(request.loadBody().getResult().asArray(),
SubscriptionUpdateRequest.class);
return getJsonResponse(
ResponseHelper.subscribeResponse(request.getHeader(AUTHORIZATION), body.channelId), "private");
} catch (Exception e) {
return getErrorResponse(e);
}
})).map("/unsubscribe", AsyncServlet.ofBlocking(executor, request -> {
try {
SubscriptionUpdateRequest body = Constants.mapper.readValue(request.loadBody().getResult().asArray(),
SubscriptionUpdateRequest.class);
return getJsonResponse(
ResponseHelper.unsubscribeResponse(request.getHeader(AUTHORIZATION), body.channelId),
"private");
} catch (Exception e) {
return getErrorResponse(e);
}
})); }));
return new CustomServletDecorator(router); return new CustomServletDecorator(router);

View file

@ -0,0 +1,33 @@
package me.kavin.piped.utils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import me.kavin.piped.utils.obj.db.User;
import me.kavin.piped.utils.obj.db.Video;
public class DatabaseSessionFactory {
private static final SessionFactory sessionFactory;
static {
final Configuration configuration = new Configuration();
configuration.setProperty("hibernate.connection.url", "jdbc:postgresql://");
configuration.setProperty("hibernate.connection.driver_class", "org.postgresql.Driver");
configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
configuration.setProperty("hibernate.connection.username", "piped");
configuration.setProperty("hibernate.connection.password", "@8LQuf7JUabCker$zQYS");
configuration.setProperty("hibernate.temp.use_jdbc_metadata_defaults", "false");
configuration.configure();
sessionFactory = configuration.addAnnotatedClass(User.class).addAnnotatedClass(Video.class)
.buildSessionFactory();
}
public static final Session createSession() {
return sessionFactory.openSession();
}
}

View file

@ -1,7 +0,0 @@
package me.kavin.piped.utils;
public class RegisterRequest {
public String username, password;
}

View file

@ -8,12 +8,19 @@ import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse.BodyHandlers; import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import org.hibernate.Session;
import org.json.JSONObject; import org.json.JSONObject;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage; import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage;
@ -58,10 +65,17 @@ import me.kavin.piped.utils.obj.StreamItem;
import me.kavin.piped.utils.obj.Streams; import me.kavin.piped.utils.obj.Streams;
import me.kavin.piped.utils.obj.StreamsPage; import me.kavin.piped.utils.obj.StreamsPage;
import me.kavin.piped.utils.obj.Subtitle; import me.kavin.piped.utils.obj.Subtitle;
import me.kavin.piped.utils.obj.db.User;
import me.kavin.piped.utils.obj.search.SearchChannel; import me.kavin.piped.utils.obj.search.SearchChannel;
import me.kavin.piped.utils.obj.search.SearchItem; import me.kavin.piped.utils.obj.search.SearchItem;
import me.kavin.piped.utils.obj.search.SearchPlaylist; import me.kavin.piped.utils.obj.search.SearchPlaylist;
import me.kavin.piped.utils.obj.search.SearchStream; import me.kavin.piped.utils.obj.search.SearchStream;
import me.kavin.piped.utils.resp.AcceptedResponse;
import me.kavin.piped.utils.resp.AlreadyRegisteredResponse;
import me.kavin.piped.utils.resp.AuthenticationFailureResponse;
import me.kavin.piped.utils.resp.DatabaseHelper;
import me.kavin.piped.utils.resp.IncorrectCredentialsResponse;
import me.kavin.piped.utils.resp.LoginResponse;
public class ResponseHelper { public class ResponseHelper {
@ -461,9 +475,105 @@ public class ResponseHelper {
} }
public static final byte[] registerResponse(String user, String pass) throws IOException { public static final byte[] registerResponse(String user, String pass)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
return Constants.mapper.writeValueAsBytes(null); user = user.toLowerCase();
Session s = DatabaseSessionFactory.createSession();
CriteriaBuilder cb = s.getCriteriaBuilder();
CriteriaQuery<User> cr = cb.createQuery(User.class);
Root<User> root = cr.from(User.class);
cr.select(root).where(root.get("username").in(user));
boolean registered = s.createQuery(cr).uniqueResult() != null;
if (registered) {
s.close();
return Constants.mapper.writeValueAsBytes(new AlreadyRegisteredResponse());
}
User newuser = new User(user, PasswordHash.createHash(pass), Collections.emptyList());
s.save(newuser);
s.beginTransaction().commit();
s.close();
return Constants.mapper.writeValueAsBytes(new LoginResponse(newuser.getSessionId()));
}
public static final byte[] loginResponse(String user, String pass)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
user = user.toLowerCase();
Session s = DatabaseSessionFactory.createSession();
CriteriaBuilder cb = s.getCriteriaBuilder();
CriteriaQuery<User> cr = cb.createQuery(User.class);
Root<User> root = cr.from(User.class);
cr.select(root).where(root.get("username").in(user));
User dbuser = s.createQuery(cr).uniqueResult();
if (dbuser != null && PasswordHash.validatePassword(pass, dbuser.getPassword())) {
s.close();
return Constants.mapper.writeValueAsBytes(new LoginResponse(dbuser.getSessionId()));
}
User newuser = new User(user, PasswordHash.createHash(pass), Collections.emptyList());
s.save(newuser);
s.beginTransaction().commit();
s.close();
return Constants.mapper.writeValueAsBytes(new IncorrectCredentialsResponse());
}
public static final byte[] subscribeResponse(String session, String channelId)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Session s = DatabaseSessionFactory.createSession();
User user = DatabaseHelper.getUserFromSession(s, session);
if (user != null) {
if (!user.getSubscribed().contains(channelId)) {
user.getSubscribed().add(channelId);
s.update(user);
s.beginTransaction().commit();
}
s.close();
return Constants.mapper.writeValueAsBytes(new AcceptedResponse());
}
s.close();
return Constants.mapper.writeValueAsBytes(new AuthenticationFailureResponse());
}
public static final byte[] unsubscribeResponse(String session, String channelId)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
Session s = DatabaseSessionFactory.createSession();
User user = DatabaseHelper.getUserFromSession(s, session);
if (user != null) {
if (user.getSubscribed().remove(channelId)) {
s.update(user);
s.beginTransaction().commit();
}
s.close();
return Constants.mapper.writeValueAsBytes(new AcceptedResponse());
}
s.close();
return Constants.mapper.writeValueAsBytes(new AuthenticationFailureResponse());
} }

View file

@ -0,0 +1,23 @@
package me.kavin.piped.utils.obj.db;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Channel {
@Id
@Column(name = "uploader_id", length = 30)
private String uploaderId;
@Column(name = "uploader", length = 80)
private String uploader;
@Column(name = "uploader_avatar", length = 150)
private String uploaderAvatar;
@Column(name = "verified")
private boolean verified;
}

View file

@ -2,6 +2,7 @@ package me.kavin.piped.utils.obj.db;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.UUID;
import javax.persistence.CollectionTable; import javax.persistence.CollectionTable;
import javax.persistence.Column; import javax.persistence.Column;
@ -32,6 +33,9 @@ public class User implements Serializable {
@Column(name = "password", columnDefinition = "text") @Column(name = "password", columnDefinition = "text")
private String password; private String password;
@Column(name = "session_id", length = 36)
private String sessionId;
@ElementCollection @ElementCollection
@CollectionTable(name = "users_subscribed", joinColumns = @JoinColumn(name = "subscriber"), indexes = @Index(columnList = "subscriber", name = "subscriber_idx")) @CollectionTable(name = "users_subscribed", joinColumns = @JoinColumn(name = "subscriber"), indexes = @Index(columnList = "subscriber", name = "subscriber_idx"))
@Column(name = "channel", length = 30) @Column(name = "channel", length = 30)
@ -44,6 +48,7 @@ public class User implements Serializable {
this.username = username; this.username = username;
this.password = password; this.password = password;
this.subscribed_ids = subscribed_ids; this.subscribed_ids = subscribed_ids;
this.sessionId = String.valueOf(UUID.randomUUID());
} }
public long getId() { public long getId() {
@ -66,6 +71,14 @@ public class User implements Serializable {
return password; return password;
} }
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public void setPassword(String password) { public void setPassword(String password) {
this.password = password; this.password = password;
} }

View file

@ -26,18 +26,6 @@ public class Video {
@Column(name = "uploaded") @Column(name = "uploaded")
private long uploaded; private long uploaded;
@Column(name = "uploader", length = 80)
private String uploader;
@Column(name = "uploader_url", length = 30)
private String uploaderUrl;
@Column(name = "uploader_avatar", length = 150)
private String uploaderAvatar;
@Column(name = "verified")
private boolean verified;
@Column(name = "thumbnail", length = 150) @Column(name = "thumbnail", length = 150)
private String thumbnail; private String thumbnail;
@ -51,10 +39,6 @@ public class Video {
this.views = views; this.views = views;
this.duration = duration; this.duration = duration;
this.uploaded = uploaded; this.uploaded = uploaded;
this.uploader = uploader;
this.uploaderUrl = uploaderUrl;
this.uploaderAvatar = uploaderAvatar;
this.verified = verified;
this.thumbnail = thumbnail; this.thumbnail = thumbnail;
} }
@ -98,38 +82,6 @@ public class Video {
this.uploaded = uploaded; this.uploaded = uploaded;
} }
public String getUploader() {
return uploader;
}
public void setUploader(String uploader) {
this.uploader = uploader;
}
public String getUploaderUrl() {
return uploaderUrl;
}
public void setUploaderUrl(String uploaderUrl) {
this.uploaderUrl = uploaderUrl;
}
public String getUploaderAvatar() {
return uploaderAvatar;
}
public void setUploaderAvatar(String uploaderAvatar) {
this.uploaderAvatar = uploaderAvatar;
}
public boolean isVerified() {
return verified;
}
public void setVerified(boolean verified) {
this.verified = verified;
}
public String getThumbnail() { public String getThumbnail() {
return thumbnail; return thumbnail;
} }

View file

@ -0,0 +1,7 @@
package me.kavin.piped.utils.resp;
public class AcceptedResponse {
public String message = "ok";
}

View file

@ -0,0 +1,7 @@
package me.kavin.piped.utils.resp;
public class AlreadyRegisteredResponse {
public String error = "The username you have used is already taken.";
}

View file

@ -0,0 +1,7 @@
package me.kavin.piped.utils.resp;
public class AuthenticationFailureResponse {
public String error = "An invalid Session ID was provided.";
}

View file

@ -0,0 +1,21 @@
package me.kavin.piped.utils.resp;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.hibernate.Session;
import me.kavin.piped.utils.obj.db.User;
public class DatabaseHelper {
public static final User getUserFromSession(Session s, String session) {
CriteriaBuilder cb = s.getCriteriaBuilder();
CriteriaQuery<User> cr = cb.createQuery(User.class);
Root<User> root = cr.from(User.class);
cr.select(root).where(root.get("sessionId").in(session));
return s.createQuery(cr).uniqueResult();
}
}

View file

@ -0,0 +1,7 @@
package me.kavin.piped.utils.resp;
public class IncorrectCredentialsResponse {
public String error = "The username or password you have entered is incorrect.";
}

View file

@ -0,0 +1,7 @@
package me.kavin.piped.utils.resp;
public class LoginRequest {
public String username, password;
}

View file

@ -0,0 +1,10 @@
package me.kavin.piped.utils.resp;
public class LoginResponse {
public String token;
public LoginResponse(String token) {
this.token = token;
}
}

View file

@ -0,0 +1,7 @@
package me.kavin.piped.utils.resp;
public class SubscriptionUpdateRequest {
public String channelId;
}

View file

@ -8,5 +8,7 @@
<!-- Optional: Show SQL output for debugging --> <!-- Optional: Show SQL output for debugging -->
<property name="hibernate.show_sql">true</property> <property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property> <property name="hibernate.format_sql">true</property>
<property name="hibernate.connection.provider_class">org.hibernate.hikaricp.internal.HikariCPConnectionProvider</property>
</session-factory> </session-factory>
</hibernate-configuration> </hibernate-configuration>