aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/assets/style.css30
-rw-r--r--src/main/java/com/juick/config/SecurityConfig.java11
-rw-r--r--src/main/java/com/juick/service/MessagesServiceImpl.java23
-rw-r--r--src/main/java/com/juick/util/MessageUtils.java28
-rw-r--r--src/main/java/com/juick/www/api/ApiSocialLogin.java89
-rw-r--r--src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java2
-rw-r--r--src/main/resources/templates/views/thread.html17
7 files changed, 144 insertions, 56 deletions
diff --git a/src/main/assets/style.css b/src/main/assets/style.css
index 99cefffa6..00f92ef3b 100644
--- a/src/main/assets/style.css
+++ b/src/main/assets/style.css
@@ -392,12 +392,12 @@ article .ir a {
display: block;
}
-article .ir img {
+article .ir img, .msg-media img {
max-width: 100%;
}
article > nav.l,
.msg-cont > nav.l {
- border-top: 1px solid var(--border-color);
+ border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-around;
@@ -490,8 +490,16 @@ article .tags > a::before,
.msg-cont {
background: var(--text-background-color);
border: 1px solid var(--border-color);
+ border-bottom: 1px solid transparent;
line-height: 140%;
- margin-bottom: 12px;
+}
+
+#replies .msg-cont {
+ border-top: 1px solid transparent;
+}
+
+#replies .msg-cont:last-child {
+ border-bottom: 1px solid var(--border-color);
}
.reply-new .msg-cont {
@@ -580,22 +588,6 @@ article .tags > a::before,
margin: 0;
}
-.title2 {
- background: var(--text-background-color);
- margin: 20px 0;
- padding: 10px 20px;
-}
-
-.title2-right {
- float: right;
- line-height: 24px;
-}
-
-#content .title2 h2 {
- font-size: x-large;
- margin: 0;
-}
-
/* #endregion */
/* #region user-generated texts */
diff --git a/src/main/java/com/juick/config/SecurityConfig.java b/src/main/java/com/juick/config/SecurityConfig.java
index a93a4a5ca..7e37b7d8e 100644
--- a/src/main/java/com/juick/config/SecurityConfig.java
+++ b/src/main/java/com/juick/config/SecurityConfig.java
@@ -35,6 +35,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -52,10 +53,10 @@ import org.springframework.security.oauth2.server.authorization.settings.Authori
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
-import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
@@ -115,9 +116,7 @@ public class SecurityConfig {
@Bean
AuthenticationEntryPoint apiAuthenticationEntryPoint() {
- var entryPoint = new BasicAuthenticationEntryPoint();
- entryPoint.setRealmName("Juick");
- return entryPoint;
+ return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED);
}
@Value("${auth_remember_me_key:secret}")
@@ -201,7 +200,7 @@ public class SecurityConfig {
"/api/swagger-ui/**",
"/api/messages/discussions",
"/api/users", "/api/thread", "/api/tags",
- "/api/tlgmbtwbhk", "/api/fbwbhk", "/api/_patreon", "/api/_vk",
+ "/api/tlgmbtwbhk", "/api/fbwbhk", "/api/_patreon", "/api/_vk", "/api/_google",
"/api/skypebotendpoint", "/api/signup",
"/api/inbox", "/api/events", "/api/u/", "/u/**",
"/n/**",
@@ -228,6 +227,7 @@ public class SecurityConfig {
}
@Bean
+ @Order(Ordered.HIGHEST_PRECEDENCE + 1)
SecurityFilterChain h2ConsoleFilterChain(HttpSecurity http) throws Exception {
http.securityMatcher("/h2-console/**")
.authorizeHttpRequests(auth -> auth
@@ -294,6 +294,7 @@ public class SecurityConfig {
}
@Bean
+ @Order(Ordered.HIGHEST_PRECEDENCE + 1)
public SecurityFilterChain securityWebFilterChain(
HttpSecurity http) throws Exception {
return http.securityMatcher("/actuator/**")
diff --git a/src/main/java/com/juick/service/MessagesServiceImpl.java b/src/main/java/com/juick/service/MessagesServiceImpl.java
index de3429774..ebb8414e2 100644
--- a/src/main/java/com/juick/service/MessagesServiceImpl.java
+++ b/src/main/java/com/juick/service/MessagesServiceImpl.java
@@ -18,7 +18,6 @@
package com.juick.service;
import com.juick.model.*;
-import com.juick.model.User;
import com.juick.www.WebApp;
import com.juick.util.MessageUtils;
import com.juick.util.TagUtils;
@@ -103,16 +102,20 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ
msg.setReplyQuote(MessageUtils.formatQuote(rs.getString(17)));
msg.setUpdated(MessagesServiceImpl.this.getOffsetDateTime(rs, 18).toInstant());
int quoteUid = rs.getInt(19);
- User quoteUser = new User();
- quoteUser.setUid(quoteUid);
- quoteUser.setName(rs.getString(20));
- if (quoteUid == 0) {
- quoteUser.setName(AnonymousUser.INSTANCE.getName());
- quoteUser.setUri(URI.create(Optional.ofNullable(rs.getString(23)).orElse(StringUtils.EMPTY)));
+ if (rs.wasNull()) {
+ msg.setTo(archiveUser);
} else {
- quoteUser.setAvatar(webApp.getAvatarUrl(quoteUser));
+ User quoteUser = new User();
+ quoteUser.setUid(quoteUid);
+ quoteUser.setName(rs.getString(20));
+ if (quoteUid == 0) {
+ quoteUser.setName(AnonymousUser.INSTANCE.getName());
+ quoteUser.setUri(URI.create(Optional.ofNullable(rs.getString(23)).orElse(StringUtils.EMPTY)));
+ } else {
+ quoteUser.setAvatar(webApp.getAvatarUrl(quoteUser));
+ }
+ msg.setTo(quoteUser);
}
- msg.setTo(quoteUser);
msg.setUpdatedAt(MessagesServiceImpl.this.getOffsetDateTime(rs, 21).toInstant());
msg.setReplyUri(URI.create(Optional.ofNullable(rs.getString(24)).orElse(StringUtils.EMPTY)));
msg.setHtml(rs.getBoolean(25));
@@ -897,7 +900,7 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ
+ "replies.user_id, users.nick, users.banned, " + "replies.ts, "
+ "0 as readonly, 0 as privacy, 0 as replies, " + "replies.attach, 0 as likes, 0 as hidden, "
+ "NULL as tags, NULL as repliesby, replies.txt, " + "COALESCE(qw.txt, m.txt) as q, " + ":now, "
- + "COALESCE(qw.user_id, m.user_id) as to_uid, COALESCE(qu.nick, mu.nick) as to_name, "
+ + "qw.user_id as to_uid, qu.nick as to_name, "
+ "replies.updated_at, replies.user_uri as uri, "
+ "qw.user_uri as to_uri, replies.reply_uri, replies.html, 0 as unread, "
+ "0 as subscribed, users.premium "
diff --git a/src/main/java/com/juick/util/MessageUtils.java b/src/main/java/com/juick/util/MessageUtils.java
index 3c8001d81..fff97f662 100644
--- a/src/main/java/com/juick/util/MessageUtils.java
+++ b/src/main/java/com/juick/util/MessageUtils.java
@@ -42,13 +42,20 @@ public class MessageUtils {
}
public static String formatQuote(final String quote) {
+ return formatQuote(quote, false);
+ }
+
+ public static String formatQuote(final String quote, final boolean isHtml) {
String result = quote;
+ var prefix = isHtml ? "<blockquote>" : ">";
+ var suffix = isHtml ? "</blockquote>" : "\n";
+
if (quote != null) {
if (quote.length() > 50) {
- result = ">" + StringUtils.abbreviate(quote, "…", 47).replace('\n', ' ') + "\n";
+ result = prefix + StringUtils.abbreviate(quote, "…", 47).replace('\n', ' ') + suffix;
} else if (!quote.isEmpty()) {
- result = ">" + quote.replace('\n', ' ') + "\n";
+ result = prefix + quote.replace('\n', ' ') + suffix;
}
}
@@ -191,8 +198,9 @@ public class MessageUtils {
// /12
// <a href="#12">/12</a>
- msg = msg.replaceAll(replyNumberRegex, "$1<a href=\"#$2\">/$2</a>$3");
-
+ if (!compatibleWithDurov) {
+ msg = msg.replaceAll(replyNumberRegex, "$1<a href=\"#$2\">/$2</a>$3");
+ }
// @username@mastodon.social
// <a href="http://juick.com/mention?username=username@mastodon.social/">@username@mastodon.social</a>
@@ -220,11 +228,11 @@ public class MessageUtils {
m.appendTail(sb);
msg = sb.toString();
+
+ // > citate
+ msg = msg.replaceAll(citateRegex, "<blockquote>$1</blockquote>");
+ msg = msg.replaceAll("</blockquote><blockquote>", "\n");
if (!compatibleWithDurov) {
- // > citate
- msg = msg.replaceAll(citateRegex, "<blockquote>$1</blockquote>");
- msg = msg.replaceAll("</blockquote><blockquote>", "\n");
-
msg = msg.replaceAll("\n", "<br/>\n");
}
return msg;
@@ -283,10 +291,6 @@ public class MessageUtils {
return URLEncoder.encode(s, StandardCharsets.UTF_8).replace("+", "%20")
.replace("*", "%2A").replace("%7E", "~");
}
- public static String formatMarkdownText(final Message msg) {
- return StringUtils.defaultString(msg.getText())
- .replaceAll(replyNumberRegex, String.format("$1[/$2](https://juick.com/m/%d#$2)$3", msg.getMid()));
- }
public static String attachmentUrl(final Message jmsg) {
if (StringUtils.isEmpty(jmsg.getAttachmentType())) {
return StringUtils.EMPTY;
diff --git a/src/main/java/com/juick/www/api/ApiSocialLogin.java b/src/main/java/com/juick/www/api/ApiSocialLogin.java
new file mode 100644
index 000000000..5b48c52be
--- /dev/null
+++ b/src/main/java/com/juick/www/api/ApiSocialLogin.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008-2024, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.juick.www.api;
+
+import com.github.scribejava.apis.GoogleTokenVerifier;
+import com.juick.model.AuthResponse;
+import com.juick.service.EmailService;
+import com.juick.service.UserService;
+import com.juick.util.HttpBadRequestException;
+import com.juick.util.HttpForbiddenException;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Controller;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import jakarta.inject.Inject;
+import java.util.Optional;
+
+@Controller
+public class ApiSocialLogin {
+
+ private static final Logger logger = LoggerFactory.getLogger(ApiSocialLogin.class);
+
+ @Value("${google_client_id:}")
+ private String googleClientId;
+
+ @Inject
+ private UserService userService;
+ @Inject
+ private EmailService emailService;
+ @Inject
+ private Users users;
+ @ResponseBody
+ @PostMapping("/api/signup")
+ public com.juick.model.User signupWithEmail(String username, String password, String verificationCode) {
+ if (username.length() < 2 || username.length() > 16 || !username.matches("^[a-zA-Z0-9\\-]+$")
+ || password.length() < 6 || password.length() > 32) {
+ throw new HttpBadRequestException();
+ }
+
+ String verifiedEmail = emailService.getEmailByAuthCode(verificationCode);
+ if (StringUtils.hasText(verifiedEmail)) {
+ com.juick.model.User newUser = userService.createUser(username, password).orElseThrow(HttpBadRequestException::new);
+ emailService.addEmail(newUser.getUid(), verifiedEmail);
+ emailService.deleteAuthCode(verificationCode);
+ return newUser;
+ } else {
+ throw new HttpForbiddenException();
+ }
+ }
+ @ResponseBody
+ @PostMapping("/api/_google")
+ public AuthResponse googleSignIn(@RequestParam(name = "idToken") String idTokenString) {
+ logger.info("Token: {}", idTokenString);
+ logger.info("Client: {}", googleClientId);
+ Optional<String> verifiedEmail = GoogleTokenVerifier.validateToken(googleClientId, idTokenString);
+ if (verifiedEmail.isPresent()) {
+ String email = verifiedEmail.get();
+ com.juick.model.User visitor = userService.getUserByEmail(email);
+ if (visitor.isAnonymous()) {
+ String verificationCode = RandomStringUtils.randomAlphanumeric(8).toUpperCase();
+ emailService.addVerificationCode(null, email, verificationCode);
+ return new AuthResponse(null, email, verificationCode);
+ } else {
+ return new AuthResponse(users.getMe(visitor), null, null);
+ }
+ }
+ throw new HttpForbiddenException();
+ }
+}
diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java b/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java
index 1ecfd40db..af3a04ad0 100644
--- a/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java
+++ b/src/main/java/com/mitchellbosecke/pebble/extension/filters/FormatMessageFilter.java
@@ -47,7 +47,7 @@ public class FormatMessageFilter implements Filter {
String toUserString = msg.getTo().getUid() == 0 ? String.format("<span class=\"h-card\"><a class=\"mention u-url\" href=\"%s\" data-user-uri=\"1\">@%s</a></span>",
msg.getTo().getUri().toASCIIString(), msg.getTo().getName()) : String.format("<span class=\"h-card\"><a class=\"u-url\" href=\"https://juick.com/%s/\">@%s</a></span>", msg.getTo().getName(), msg.getTo().getName());
String formatString = MessageUtils.replyStartsWithQuote(msg) ? "%s,\n%s" : "%s, %s";
- String msgTxt = msg.getRid() > 0 ? String.format(formatString, toUserString, formattedMessage)
+ String msgTxt = ((msg.getRid() > 0) && msg.getReplyto() > 0) ? String.format(formatString, toUserString, formattedMessage)
: formattedMessage;
return new SafeString(msgTxt);
}
diff --git a/src/main/resources/templates/views/thread.html b/src/main/resources/templates/views/thread.html
index ab7512947..bb4977308 100644
--- a/src/main/resources/templates/views/thread.html
+++ b/src/main/resources/templates/views/thread.html
@@ -6,7 +6,7 @@
<div class="msg-cont">
<div class="msg-header">
<div class="msg-avatar">
- <a href="/{{ msg.user.name }}/"><img src="{{ msg.user.avatar }}" alt="{{ msg.user.name }}"/></a>
+ <a href="/{{ msg.user.name }}/"><img src="{{ msg.user.avatar }}" alt="{{ msg.user.name }}"></a>
</div>
<span>
<a href="/{{ msg.user.name }}/"><span>{{ msg.user.name }}</span>{% if msg.user.premium %}<span style="color: green;"><i data-icon="ei-star" data-size="s"></i></span>{% endif %}</a>
@@ -35,7 +35,7 @@
{% if msg.AttachmentType is not empty %}
<div class="msg-media">
<a href="//i.juick.com/p/{{ msg.mid }}.{{ msg.AttachmentType }}" data-fname="{{ msg.mid }}.{{ msg.AttachmentType }}">
- <img src="//i.juick.com/photos-512/{{ msg.mid }}.{{ msg.AttachmentType }}" alt=""/>
+ <img src="//i.juick.com/photos-512/{{ msg.mid }}.{{ msg.AttachmentType }}" alt="">
</a>
</div>
{% endif %}
@@ -102,7 +102,7 @@
</nav>
{% if msg.VisitorCanComment %}
<form class="msg-comment-target">
- <input type="hidden" name="mid" value="{{ msg.mid }}"/>
+ <input type="hidden" name="mid" value="{{ msg.mid }}">
<div class="msg-comment">
<div class="ta-wrapper">
<textarea name="body" rows="1" class="reply" placeholder="{{ i18n("messages","message.writeComment") }}"></textarea>
@@ -124,11 +124,10 @@
</div>
</li>
</ul>
-<div class="title2">
+<div style="display:none;">
{% if visitor.uid > 0 %}
- <img src="/api/thread/mark_read/{{ msg.mid }}-{{ msg.rid }}.gif?hash={{visitor.authHash}}" />
+ <img src="/api/thread/mark_read/{{ msg.mid }}-{{ msg.rid }}.gif">
{% endif %}
- <h2>{{ i18n("messages","reply.replies") }} ({{ replies.size() }})</h2>
</div>
<ul id="replies">
@@ -140,13 +139,13 @@
<a class="a-username" href="/{{ msg.user.name }}/">{{ msg.user.name }}{% if msg.user.premium %}<span style="color: green;"><i data-icon="ei-star" data-size="s"></i></span>{% endif %}</a>
<div class="msg-avatar">
<a class="a-username" href="/{{ msg.user.name }}/">
- <img src="{{ msg.user.avatar }}" alt="{{ msg.user.name }}"/>
+ <img src="{{ msg.user.avatar }}" alt="{{ msg.user.name }}">
</a>
</div>
{% else %}
[удалено]:
<div class="msg-avatar">
- <img src="//i.juick.com/av-96.png"/>
+ <img src="//i.juick.com/av-96.png">
</div>
{% endif %}
<div class="msg-ts">
@@ -165,7 +164,7 @@
{% if msg.AttachmentType is not empty %}
<div class="msg-media">
<a href="//i.juick.com/p/{{ msg.mid }}-{{ msg.rid }}.{{ msg.AttachmentType }}" data-fname="{{ msg.mid }}-{{ msg.rid }}.{{ msg.AttachmentType }}">
- <img src="//i.juick.com/photos-512/{{ msg.mid }}-{{ msg.rid }}.{{ msg.AttachmentType }}" alt=""/>
+ <img src="//i.juick.com/photos-512/{{ msg.mid }}-{{ msg.rid }}.{{ msg.AttachmentType }}" alt="">
</a>
</div>
{% endif %}