aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/assets/icon.js4
-rw-r--r--src/main/assets/scripts.js14
-rw-r--r--src/main/assets/style.css10
-rw-r--r--src/main/assets/sw.js (renamed from src/main/resources/static/sw.js)0
-rw-r--r--src/main/java/com/cliqset/xrd/Alias.java63
-rw-r--r--src/main/java/com/cliqset/xrd/Expires.java58
-rw-r--r--src/main/java/com/cliqset/xrd/Link.java151
-rw-r--r--src/main/java/com/cliqset/xrd/Property.java75
-rw-r--r--src/main/java/com/cliqset/xrd/Signature.java21
-rw-r--r--src/main/java/com/cliqset/xrd/Subject.java62
-rw-r--r--src/main/java/com/cliqset/xrd/Title.java68
-rw-r--r--src/main/java/com/cliqset/xrd/XRD.java166
-rw-r--r--src/main/java/com/cliqset/xrd/XRDConstants.java26
-rw-r--r--src/main/java/com/cliqset/xrd/XRDException.java35
-rw-r--r--src/main/java/com/github/scribejava/apis/AppleSignInApi.java3
-rw-r--r--src/main/java/com/juick/ActivityPubManager.java8
-rw-r--r--src/main/java/com/juick/CommandsManager.java9
-rw-r--r--src/main/java/com/juick/EmailManager.java7
-rw-r--r--src/main/java/com/juick/PremiumManager.java43
-rw-r--r--src/main/java/com/juick/ServerManager.java4
-rw-r--r--src/main/java/com/juick/TelegramBotManager.java4
-rw-r--r--src/main/java/com/juick/TopManager.java2
-rw-r--r--src/main/java/com/juick/TwitterManager.java2
-rw-r--r--src/main/java/com/juick/XMPPManager.java2
-rw-r--r--src/main/java/com/juick/config/ActivityPubConfig.java2
-rw-r--r--src/main/java/com/juick/config/AppConfig.java12
-rw-r--r--src/main/java/com/juick/config/HttpClientConfig.java3
-rw-r--r--src/main/java/com/juick/config/SecurityConfig.java7
-rw-r--r--src/main/java/com/juick/config/WebConfig.java2
-rw-r--r--src/main/java/com/juick/config/XMPPConfig.java2
-rw-r--r--src/main/java/com/juick/data/MessagesRepository.java24
-rw-r--r--src/main/java/com/juick/data/UserMapper.java42
-rw-r--r--src/main/java/com/juick/data/UsersRepository.java39
-rw-r--r--src/main/java/com/juick/data/entities/BaseMessagePropertyEntity.java (renamed from src/main/java/com/cliqset/xrd/package-info.java)29
-rw-r--r--src/main/java/com/juick/data/entities/BaseUserPropertyEntity.java34
-rw-r--r--src/main/java/com/juick/data/entities/EmailEntity.java36
-rw-r--r--src/main/java/com/juick/data/entities/FacebookEntity.java36
-rw-r--r--src/main/java/com/juick/data/entities/LoginEntity.java36
-rw-r--r--src/main/java/com/juick/data/entities/MessageEntity.java99
-rw-r--r--src/main/java/com/juick/data/entities/ReplyEntity.java57
-rw-r--r--src/main/java/com/juick/data/entities/TagEntity.java60
-rw-r--r--src/main/java/com/juick/data/entities/TelegramEntity.java36
-rw-r--r--src/main/java/com/juick/data/entities/UserEntity.java148
-rw-r--r--src/main/java/com/juick/data/entities/VKEntity.java36
-rw-r--r--src/main/java/com/juick/model/User.java13
-rw-r--r--src/main/java/com/juick/service/ActivityPubService.java4
-rw-r--r--src/main/java/com/juick/service/BaseJdbcService.java2
-rw-r--r--src/main/java/com/juick/service/EmailService.java3
-rw-r--r--src/main/java/com/juick/service/EmailServiceImpl.java36
-rw-r--r--src/main/java/com/juick/service/InfoService.java2
-rw-r--r--src/main/java/com/juick/service/MessagesServiceImpl.java6
-rw-r--r--src/main/java/com/juick/service/PatreonService.java35
-rw-r--r--src/main/java/com/juick/service/PostgresSearchService.java2
-rw-r--r--src/main/java/com/juick/service/SocialService.java2
-rw-r--r--src/main/java/com/juick/service/SphinxSearchService.java2
-rw-r--r--src/main/java/com/juick/service/SubscriptionServiceImpl.java4
-rw-r--r--src/main/java/com/juick/service/TagServiceImpl.java3
-rw-r--r--src/main/java/com/juick/service/TelegramService.java2
-rw-r--r--src/main/java/com/juick/service/TwitterService.java7
-rw-r--r--src/main/java/com/juick/service/UserService.java5
-rw-r--r--src/main/java/com/juick/service/UserServiceImpl.java49
-rw-r--r--src/main/java/com/juick/service/VKService.java20
-rw-r--r--src/main/java/com/juick/service/WebfingerService.java2
-rw-r--r--src/main/java/com/juick/service/component/SystemEvent.java2
-rw-r--r--src/main/java/com/juick/util/MessageUtils.java38
-rw-r--r--src/main/java/com/juick/util/formatters/PlainTextFormatter.java4
-rw-r--r--src/main/java/com/juick/util/xmpp/JidConverter.java3
-rw-r--r--src/main/java/com/juick/www/VisitorArgumentResolver.java2
-rw-r--r--src/main/java/com/juick/www/WebApp.java2
-rw-r--r--src/main/java/com/juick/www/ad/SapeService.java2
-rw-r--r--src/main/java/com/juick/www/api/ApiSocialLogin.java299
-rw-r--r--src/main/java/com/juick/www/api/Mastodon.java4
-rw-r--r--src/main/java/com/juick/www/api/Messages.java2
-rw-r--r--src/main/java/com/juick/www/api/Notifications.java2
-rw-r--r--src/main/java/com/juick/www/api/PM.java2
-rw-r--r--src/main/java/com/juick/www/api/Post.java74
-rw-r--r--src/main/java/com/juick/www/api/Service.java6
-rw-r--r--src/main/java/com/juick/www/api/SystemActivity.java2
-rw-r--r--src/main/java/com/juick/www/api/Tags.java2
-rw-r--r--src/main/java/com/juick/www/api/Users.java2
-rw-r--r--src/main/java/com/juick/www/api/activity/Profile.java2
-rw-r--r--src/main/java/com/juick/www/api/hostmeta/HostMeta.java43
-rw-r--r--src/main/java/com/juick/www/api/webfinger/Resource.java47
-rw-r--r--src/main/java/com/juick/www/api/webhooks/PatreonWebhook.java26
-rw-r--r--src/main/java/com/juick/www/api/webhooks/TelegramWebhook.java2
-rw-r--r--src/main/java/com/juick/www/api/webhooks/VkWebhook.java8
-rw-r--r--src/main/java/com/juick/www/api/xnodeinfo2/Info.java27
-rw-r--r--src/main/java/com/juick/www/controllers/Compat.java2
-rw-r--r--src/main/java/com/juick/www/controllers/Help.java2
-rw-r--r--src/main/java/com/juick/www/controllers/Settings.java64
-rw-r--r--src/main/java/com/juick/www/controllers/SignUp.java3
-rw-r--r--src/main/java/com/juick/www/controllers/Site.java4
-rw-r--r--src/main/java/com/juick/www/controllers/SocialLogin.java84
-rw-r--r--src/main/java/com/juick/www/filters/AnythingFilter.java2
-rw-r--r--src/main/java/com/juick/www/rss/Feeds.java2
-rw-r--r--src/main/java/com/juick/www/rss/MessagesView.java2
-rw-r--r--src/main/java/com/juick/www/rss/RepliesView.java2
-rw-r--r--src/main/resources/application.properties3
-rw-r--r--src/main/resources/data-h2.sql1
-rw-r--r--src/main/resources/db/migration/V1.50__email_created_at.sql1
-rw-r--r--src/main/resources/db/migration/V1.51__cast_users_lang.sql2
-rw-r--r--src/main/resources/schema-h2.sql3
-rw-r--r--src/main/resources/schema-mysql.sql1
-rw-r--r--src/main/resources/schema-sqlite.sql1
-rw-r--r--src/main/resources/schema-sqlserver.sql1
-rw-r--r--src/main/resources/templates/layouts/default.html53
-rw-r--r--src/main/resources/templates/views/thread.html2
107 files changed, 1153 insertions, 1461 deletions
diff --git a/src/main/assets/icon.js b/src/main/assets/icon.js
index 5cd787a5..b4b5aedc 100644
--- a/src/main/assets/icon.js
+++ b/src/main/assets/icon.js
@@ -1,10 +1,10 @@
-import evilIcons from 'evil-icons/assets/sprite.svg'
+const spritesUrl = new URL('evil-icons/assets/sprite.svg', import.meta.url)
function icon(name, { size = '', className = '' }) {
const classes = `icon icon--${name} icon--${size} ${className}`.trim()
var icon = '<svg class="icon__cnt">' +
- `<use xlink:href='${evilIcons}#${name}-icon' />` +
+ `<use xlink:href='${spritesUrl}#${name}-icon' />` +
'</svg>'
var html = '<div class="' + classes + '">' +
diff --git a/src/main/assets/scripts.js b/src/main/assets/scripts.js
index 3c83bba4..12c4d9b2 100644
--- a/src/main/assets/scripts.js
+++ b/src/main/assets/scripts.js
@@ -251,6 +251,10 @@ function closeDialogListener(ev) {
}
}
+/**
+ *
+ * @param {Response} response
+ */
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText)
@@ -310,15 +314,14 @@ function showCommentForm(mid, rid) {
method: 'POST',
body: formData,
credentials: 'include'
- }).then(handleErrors)
- .then(response => response.json())
+ }).then(response => response.json())
.then(result => {
if (result.newMessage) {
window.location.hash = `#${result.newMessage.rid}`
+ window.location.reload()
} else {
alert(result.text)
}
- window.location.reload()
}).catch(error => {
alert(error.message)
})
@@ -535,7 +538,7 @@ function fetchUserUri(dataUri) {
const registerServiceWorker = () => {
const publicKey = 'BPU0LniKKR0QiaUvILPd9AystmSOU8rWDZobxKm7IJN5HYxOSQdktRdc74TZvyRS9_kyUz7LDN6gUAmAVOmObAU'
- navigator.serviceWorker.register('/sw.js', { scope: '/' })
+ navigator.serviceWorker.register(new URL('./sw.js', import.meta.url), { scope: '/' })
navigator.serviceWorker.ready.then(reg => {
return reg.pushManager.subscribe({
userVisibleOnly: true,
@@ -663,8 +666,7 @@ ready(() => {
method: 'POST',
body: formData,
credentials: 'include'
- }).then(handleErrors)
- .then(response => response.json())
+ }).then(response => response.json())
.then(result => {
if (result.newMessage) {
window.location.href = new URL(`/m/${result.newMessage.mid}`, window.location.href).href
diff --git a/src/main/assets/style.css b/src/main/assets/style.css
index f5e4ef00..99cefffa 100644
--- a/src/main/assets/style.css
+++ b/src/main/assets/style.css
@@ -363,8 +363,7 @@ article {
background: var(--text-background-color);
border: 1px solid var(--border-color);
line-height: 140%;
- margin-bottom: 10px;
- overflow: auto;
+ margin-bottom: 10px;
}
article .h,
article #postmsg,
@@ -375,6 +374,9 @@ article #postmsg,
.msg-cont .msg-header {
padding: 20px;
}
+.page-content {
+ overflow: auto;
+}
article time {
color: #88958d;
@@ -443,6 +445,10 @@ article .tags > a::before,
padding: 0 10px;
}
+.dimmed {
+ color: var(--dimmed-link-color);
+}
+
.l .msg-button {
padding: 12px;
}
diff --git a/src/main/resources/static/sw.js b/src/main/assets/sw.js
index a64de7cc..a64de7cc 100644
--- a/src/main/resources/static/sw.js
+++ b/src/main/assets/sw.js
diff --git a/src/main/java/com/cliqset/xrd/Alias.java b/src/main/java/com/cliqset/xrd/Alias.java
deleted file mode 100644
index 3a108f46..00000000
--- a/src/main/java/com/cliqset/xrd/Alias.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- Copyright 2010 Cliqset Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.cliqset.xrd;
-
-import java.net.URI;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.xml.namespace.QName;
-
-import jakarta.xml.bind.annotation.XmlAccessType;
-import jakarta.xml.bind.annotation.XmlAccessorType;
-import jakarta.xml.bind.annotation.XmlAnyAttribute;
-import jakarta.xml.bind.annotation.XmlType;
-import jakarta.xml.bind.annotation.XmlValue;
-
-@XmlType(name="Alias", namespace=XRDConstants.XRD_NAMESPACE)
-@XmlAccessorType(XmlAccessType.FIELD)
-public class Alias {
-
- @XmlAnyAttribute()
- private Map<QName, Object> unknownAttributes;
-
- @XmlValue
- private URI value;
-
- public void setValue(URI value) {
- this.value = value;
- }
-
- public URI getValue() {
- return value;
- }
-
- public void setUnknownAttributes(Map<QName, Object> unknownAttributes) {
- this.unknownAttributes = unknownAttributes;
- }
-
- public Map<QName, Object> getUnknownAttributes() {
- if (null == this.unknownAttributes) {
- this.unknownAttributes = new HashMap<QName, Object>();
- }
- return unknownAttributes;
- }
-
- public boolean hasUnknownAttributes() {
- return !(null == this.unknownAttributes || this.unknownAttributes.size() < 1);
- }
-}
diff --git a/src/main/java/com/cliqset/xrd/Expires.java b/src/main/java/com/cliqset/xrd/Expires.java
deleted file mode 100644
index bb6704f8..00000000
--- a/src/main/java/com/cliqset/xrd/Expires.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- Copyright 2010 Cliqset Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.cliqset.xrd;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.xml.namespace.QName;
-import jakarta.xml.bind.annotation.*;
-
-@XmlType(name="Expires", namespace=XRDConstants.XRD_NAMESPACE)
-@XmlAccessorType(XmlAccessType.FIELD)
-public class Expires {
-
- @XmlAnyAttribute
- private Map<QName, Object> unknownAttributes;
-
- @XmlValue
- private Date value;
-
- public void setValue(Date value) {
- this.value = value;
- }
-
- public Date getValue() {
- return value;
- }
-
- public void setUnknownAttributes(Map<QName, Object> unknownAttributes) {
- this.unknownAttributes = unknownAttributes;
- }
-
- public Map<QName, Object> getUnknownAttributes() {
- if (null == this.unknownAttributes) {
- this.unknownAttributes = new HashMap<QName, Object>();
- }
- return unknownAttributes;
- }
-
- public boolean hasUnknownAttributes() {
- return !(null == this.unknownAttributes || this.unknownAttributes.size() < 1);
- }
-}
diff --git a/src/main/java/com/cliqset/xrd/Link.java b/src/main/java/com/cliqset/xrd/Link.java
deleted file mode 100644
index 020defe6..00000000
--- a/src/main/java/com/cliqset/xrd/Link.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- Copyright 2010 Cliqset Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.cliqset.xrd;
-
-import java.net.URI;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.xml.namespace.QName;
-import jakarta.xml.bind.annotation.XmlAnyElement;
-import jakarta.xml.bind.annotation.XmlAnyAttribute;
-import jakarta.xml.bind.annotation.XmlType;
-import jakarta.xml.bind.annotation.XmlAttribute;
-import jakarta.xml.bind.annotation.XmlElement;
-import jakarta.xml.bind.annotation.XmlAccessType;
-import jakarta.xml.bind.annotation.XmlAccessorType;
-
-import org.w3c.dom.Element;
-
-@XmlType(name="Link", namespace=XRDConstants.XRD_NAMESPACE)
-@XmlAccessorType(XmlAccessType.FIELD)
-public class Link {
-
- @XmlAttribute(name="rel")
- private URI rel;
-
- @XmlAttribute(name="type")
- private String type;
-
- @XmlAttribute(name="href")
- private URI href;
-
- @XmlAttribute(name="template")
- private String template;
-
- @XmlElement(name="Title")
- private List<Title> titles;
-
- @XmlElement(name="Property")
- private List<Property> properties;
-
- @XmlAnyElement
- private List<Element> unknownElements;
-
- @XmlAnyAttribute
- private Map<QName,Object> unknownAttributes;
-
- private URI processedTemplate;
-
- public void setRel(URI rel) {
- this.rel = rel;
- }
-
- public URI getRel() {
- return rel;
- }
-
- public void setType(String type) {
- this.type = type;
- }
-
- public String getType() {
- return type;
- }
-
- public void setHref(URI href) {
- this.href = href;
- }
-
- public URI getHref() {
- return href;
- }
-
- public void setTemplate(String template) {
- this.template = template;
- }
-
- public String getTemplate() {
- return template;
- }
-
- public void setTitles(List<Title> titles) {
- this.titles = titles;
- }
-
- public List<Title> getTitles() {
- return titles;
- }
-
- public void setProperties(List<Property> properties) {
- this.properties = properties;
- }
-
- public List<Property> getProperties() {
- return properties;
- }
-
- public void setUnknownElements(List<Element> unknownElements) {
- this.unknownElements = unknownElements;
- }
-
- public List<Element> getUnknownElements() {
- return unknownElements;
- }
-
- public void setUnknownAttributes(Map<QName,Object> unknownAttributes) {
- this.unknownAttributes = unknownAttributes;
- }
-
- public Map<QName,Object> getUnknownAttributes() {
- if (null == this.unknownAttributes) {
- this.unknownAttributes = new HashMap<QName, Object>();
- }
- return unknownAttributes;
- }
-
- public boolean hasTemplate() {
- return null != this.template;
- }
-
- public boolean hasHref() {
- return null != this.href;
- }
-
- public boolean hasUnknownAttributes() {
- return !(null == this.unknownAttributes || this.unknownAttributes.size() < 1);
- }
-
- public void setProcessedTemplate(URI processedTemplate) {
- this.processedTemplate = processedTemplate;
- }
-
- public URI getProcessedTemplate() {
- return processedTemplate;
- }
-}
diff --git a/src/main/java/com/cliqset/xrd/Property.java b/src/main/java/com/cliqset/xrd/Property.java
deleted file mode 100644
index f7777c94..00000000
--- a/src/main/java/com/cliqset/xrd/Property.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- Copyright 2010 Cliqset Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.cliqset.xrd;
-
-import java.net.URI;
-
-import jakarta.xml.bind.annotation.XmlAccessType;
-import jakarta.xml.bind.annotation.XmlAccessorType;
-import jakarta.xml.bind.annotation.XmlAnyAttribute;
-import jakarta.xml.bind.annotation.XmlAttribute;
-import jakarta.xml.bind.annotation.XmlType;
-import jakarta.xml.bind.annotation.XmlValue;
-import javax.xml.namespace.QName;
-
-import java.util.HashMap;
-import java.util.Map;
-
-@XmlType(name="Property", namespace=XRDConstants.XRD_NAMESPACE)
-@XmlAccessorType(XmlAccessType.FIELD)
-public class Property {
-
- @XmlAttribute(name="type", required=true)
- private URI type;
-
- @XmlAnyAttribute()
- private Map<QName, Object> unknownAttributes;
-
- @XmlValue()
- private URI value;
-
- public void setType(URI type) {
- this.type = type;
- }
-
- public URI getType() {
- return type;
- }
-
- public void setValue(URI value) {
- this.value = value;
- }
-
- public URI getValue() {
- return value;
- }
-
- public void setUnknownAttributes(Map<QName, Object> unknownAttributes) {
- this.unknownAttributes = unknownAttributes;
- }
-
- public Map<QName, Object> getUnknownAttributes() {
- if (null == this.unknownAttributes) {
- this.unknownAttributes = new HashMap<QName, Object>();
- }
- return unknownAttributes;
- }
-
- public boolean hasUnknownAttributes() {
- return !(null == this.unknownAttributes || this.unknownAttributes.size() < 1);
- }
-}
diff --git a/src/main/java/com/cliqset/xrd/Signature.java b/src/main/java/com/cliqset/xrd/Signature.java
deleted file mode 100644
index f52f9218..00000000
--- a/src/main/java/com/cliqset/xrd/Signature.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- Copyright 2010 Cliqset Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.cliqset.xrd;
-
-public class Signature {
-
-}
diff --git a/src/main/java/com/cliqset/xrd/Subject.java b/src/main/java/com/cliqset/xrd/Subject.java
deleted file mode 100644
index d2f6b333..00000000
--- a/src/main/java/com/cliqset/xrd/Subject.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- Copyright 2010 Cliqset Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.cliqset.xrd;
-
-import jakarta.xml.bind.annotation.XmlAccessType;
-import jakarta.xml.bind.annotation.XmlAccessorType;
-import jakarta.xml.bind.annotation.XmlAnyAttribute;
-import jakarta.xml.bind.annotation.XmlType;
-import jakarta.xml.bind.annotation.XmlValue;
-import javax.xml.namespace.QName;
-
-import java.net.URI;
-import java.util.HashMap;
-import java.util.Map;
-
-@XmlType(name="Subject", namespace=XRDConstants.XRD_NAMESPACE)
-@XmlAccessorType(XmlAccessType.FIELD)
-public class Subject {
-
- @XmlAnyAttribute()
- private Map<QName, Object> unknownAttributes;
-
- @XmlValue
- private URI value;
-
- public void setValue(URI value) {
- this.value = value;
- }
-
- public URI getValue() {
- return value;
- }
-
- public void setUnknownAttributes(Map<QName, Object> unknownAttributes) {
- this.unknownAttributes = unknownAttributes;
- }
-
- public Map<QName, Object> getUnknownAttributes() {
- if (null == this.unknownAttributes) {
- this.unknownAttributes = new HashMap<QName, Object>();
- }
- return unknownAttributes;
- }
-
- public boolean hasUnknownAttributes() {
- return !(null == this.unknownAttributes || this.unknownAttributes.size() < 1);
- }
-}
diff --git a/src/main/java/com/cliqset/xrd/Title.java b/src/main/java/com/cliqset/xrd/Title.java
deleted file mode 100644
index 4bb43c55..00000000
--- a/src/main/java/com/cliqset/xrd/Title.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- Copyright 2010 Cliqset Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.cliqset.xrd;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import jakarta.xml.bind.annotation.*;
-import javax.xml.namespace.QName;
-
-@XmlType(name="Title", namespace=XRDConstants.XRD_NAMESPACE)
-@XmlAccessorType(XmlAccessType.FIELD)
-public class Title {
-
- @XmlAttribute(name="lang", namespace=XRDConstants.XML_NAMESPACE)
- private String lang;
-
- @XmlAnyAttribute()
- private Map<QName, Object> unknownAttributes;
-
- @XmlValue
- private String value;
-
- public void setValue(String value) {
- this.value = value;
- }
-
- public String getValue() {
- return value;
- }
-
- public void setLang(String lang) {
- this.lang = lang;
- }
-
- public String getLang() {
- return lang;
- }
-
- public void setUnknownAttributes(Map<QName, Object> unknownAttributes) {
- this.unknownAttributes = unknownAttributes;
- }
-
- public Map<QName, Object> getUnknownAttributes() {
- if (null == this.unknownAttributes) {
- this.unknownAttributes = new HashMap<QName, Object>();
- }
- return unknownAttributes;
- }
-
- public boolean hasUnknownAttributes() {
- return !(null == this.unknownAttributes || this.unknownAttributes.size() < 1);
- }
-}
diff --git a/src/main/java/com/cliqset/xrd/XRD.java b/src/main/java/com/cliqset/xrd/XRD.java
deleted file mode 100644
index 8fc6f7de..00000000
--- a/src/main/java/com/cliqset/xrd/XRD.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- Copyright 2010 Cliqset Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.cliqset.xrd;
-
-import javax.xml.namespace.QName;
-import jakarta.xml.bind.annotation.*;
-import jakarta.xml.bind.JAXBContext;
-import jakarta.xml.bind.JAXBException;
-
-import org.w3c.dom.Element;
-
-import java.io.InputStream;
-import java.util.List;
-import java.util.Map;
-
-@XmlRootElement(name="XRD", namespace=XRDConstants.XRD_NAMESPACE)
-@XmlAccessorType(XmlAccessType.FIELD)
-public class XRD {
-
- @XmlAttribute(name="id", namespace=XRDConstants.XML_NAMESPACE)
- private String id;
-
- @XmlAnyAttribute
- private Map<QName, Object> unknownAttributes;
-
- @XmlElement(name="Expires", namespace=XRDConstants.XRD_NAMESPACE)
- private Expires expires;
-
- @XmlElement(name="Subject", namespace=XRDConstants.XRD_NAMESPACE)
- private Subject subject;
-
- @XmlElement(name="Alias", namespace=XRDConstants.XRD_NAMESPACE)
- private List<Alias> aliases;
-
- @XmlElement(name="Property", namespace=XRDConstants.XRD_NAMESPACE)
- private List<Property> properties;
-
- @XmlElement(name="Link", namespace=XRDConstants.XRD_NAMESPACE)
- private List<Link> links;
-
- @XmlElement(name="Signature", namespace=XRDConstants.XML_SIG_NAMESPACE)
- private List<Signature> signatures;
-
- @XmlAnyElement
- private List<Element> unknownElements;
-
- public void setExpires(Expires expires) {
- this.expires = expires;
- }
-
- public Expires getExpires() {
- return expires;
- }
-
- public void setSubject(Subject subject) {
- this.subject = subject;
- }
-
- public Subject getSubject() {
- return subject;
- }
-
- public void setAliases(List<Alias> aliases) {
- this.aliases = aliases;
- }
-
- public List<Alias> getAliases() {
- return aliases;
- }
-
- public void setProperties(List<Property> properties) {
- this.properties = properties;
- }
-
- public List<Property> getProperties() {
- return properties;
- }
-
- public void setLinks(List<Link> links) {
- this.links = links;
- }
-
- public List<Link> getLinks() {
- return links;
- }
-
- public void setSignatures(List<Signature> signatures) {
- this.signatures = signatures;
- }
-
- public List<Signature> getSignatures() {
- return signatures;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public String getId() {
- return id;
- }
-
- public void setUnknownAttributes(Map<QName, Object> unknownAttributes) {
- this.unknownAttributes = unknownAttributes;
- }
-
- public Map<QName, Object> getUnknownAttributes() {
- return unknownAttributes;
- }
-
- public void setUnknownElements(List<Element> unknownElements) {
- this.unknownElements = unknownElements;
- }
-
- public List<Element> getUnknownElements() {
- return unknownElements;
- }
-
- public boolean hasId() {
- return null != this.id;
- }
-
- public boolean hasExpires() {
- return null != this.expires;
- }
-
- public boolean hasSubject() {
- return null != this.subject;
- }
-
- public boolean hasAliases() {
- return null != this.aliases;
- }
-
- public boolean hasProperties() {
- return null != this.properties;
- }
-
- public boolean hasLinks() {
- return null != this.links;
- }
-
- public static XRD fromStream(InputStream stream) throws XRDException {
- JAXBContext context;
- try {
- context = JAXBContext.newInstance(XRD.class);
- return (XRD)context.createUnmarshaller().unmarshal(stream);
- } catch (JAXBException e) {
- throw new XRDException("Unable to deserialize stream into XRD", e);
- }
- }
-}
diff --git a/src/main/java/com/cliqset/xrd/XRDConstants.java b/src/main/java/com/cliqset/xrd/XRDConstants.java
deleted file mode 100644
index 39e3c584..00000000
--- a/src/main/java/com/cliqset/xrd/XRDConstants.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- Copyright 2010 Cliqset Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.cliqset.xrd;
-
-public class XRDConstants {
-
- public static final String XRD_NAMESPACE = "http://docs.oasis-open.org/ns/xri/xrd-1.0";
- public static final String XML_SIG_NAMESPACE = "";
- public static final String XML_NAMESPACE = "";
-
- public static final String XRD_MEDIA_TYPE = "application/xrd+xml";
-}
diff --git a/src/main/java/com/cliqset/xrd/XRDException.java b/src/main/java/com/cliqset/xrd/XRDException.java
deleted file mode 100644
index da1e6849..00000000
--- a/src/main/java/com/cliqset/xrd/XRDException.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- Copyright 2010 Cliqset Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package com.cliqset.xrd;
-
-@SuppressWarnings("serial")
-public class XRDException extends Exception {
-
- public XRDException() {}
-
- public XRDException(String message) {
- super(message);
- }
-
- public XRDException(Throwable cause) {
- super(cause);
- }
-
- public XRDException(String message, Throwable cause) {
- super(message, cause);
- }
-}
diff --git a/src/main/java/com/github/scribejava/apis/AppleSignInApi.java b/src/main/java/com/github/scribejava/apis/AppleSignInApi.java
index 5d11a2a6..9e487ca0 100644
--- a/src/main/java/com/github/scribejava/apis/AppleSignInApi.java
+++ b/src/main/java/com/github/scribejava/apis/AppleSignInApi.java
@@ -114,7 +114,8 @@ public class AppleSignInApi extends DefaultApi20 {
}
String email = (String) claimsSet.get("email");
- boolean verified = claimsSet.get("email_verified").equals("true");
+ boolean verified = claimsSet.get("email_verified").equals(true);
+ logger.debug("Email {} verified: {}", email, verified);
return verified ? Optional.of(email) : Optional.empty();
}
}
diff --git a/src/main/java/com/juick/ActivityPubManager.java b/src/main/java/com/juick/ActivityPubManager.java
index 6837a416..a581ef0d 100644
--- a/src/main/java/com/juick/ActivityPubManager.java
+++ b/src/main/java/com/juick/ActivityPubManager.java
@@ -50,23 +50,19 @@ import org.springframework.http.MediaType;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.tomitribe.auth.signatures.Algorithm;
-import org.tomitribe.auth.signatures.MissingRequiredHeaderException;
import org.tomitribe.auth.signatures.Signature;
import org.tomitribe.auth.signatures.Verifier;
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
+import jakarta.annotation.Nonnull;
+import jakarta.inject.Inject;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URI;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
-import java.security.SignatureException;
import java.time.Instant;
import java.util.*;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
public class ActivityPubManager implements ActivityListener, NotificationListener {
diff --git a/src/main/java/com/juick/CommandsManager.java b/src/main/java/com/juick/CommandsManager.java
index 7488e331..a0f6e5e5 100644
--- a/src/main/java/com/juick/CommandsManager.java
+++ b/src/main/java/com/juick/CommandsManager.java
@@ -30,8 +30,8 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
+import jakarta.annotation.Nonnull;
+import jakarta.inject.Inject;
import com.juick.model.CommandResult;
import com.juick.model.Message;
@@ -545,7 +545,7 @@ public class CommandsManager {
}
}
Pair<String, Set<Tag>> messageTags = tagService.fromString(txt);
- if (user.getUid() == msg.get().getUser().getUid() && rid == 0 && messageTags.getRight().size() > 0) {
+ if (user.getUid() == msg.get().getUser().getUid() && rid == 0 && !messageTags.getRight().isEmpty()) {
var updatedTags = tagService.updateTags(mid, messageTags.getRight());
if (!CollectionUtils.isEqualCollection(updatedTags, msg.get().getTags())) {
messagesService.setReadOnly(msg.get().getMid(), TagUtils.hasTag(updatedTags, "readonly"));
@@ -554,6 +554,9 @@ public class CommandsManager {
return CommandResult.fromString("Tags are NOT updated (5 tags maximum?)");
}
} else {
+ if (!user.isVerified()) {
+ return CommandResult.fromString("Please, verify your account at https://juick.com/settings");
+ }
if (!messagesService.canViewThread(mid, user.getUid())) {
return CommandResult.fromString("Message unavailable");
}
diff --git a/src/main/java/com/juick/EmailManager.java b/src/main/java/com/juick/EmailManager.java
index 6958d970..37c8e3c5 100644
--- a/src/main/java/com/juick/EmailManager.java
+++ b/src/main/java/com/juick/EmailManager.java
@@ -33,7 +33,6 @@ import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMultipart;
import com.juick.util.HttpBadRequestException;
-import com.juick.www.WebApp;
import com.juick.service.EmailService;
import com.juick.service.MessagesService;
import com.juick.service.UserService;
@@ -47,8 +46,8 @@ import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
+import jakarta.annotation.Nonnull;
+import jakarta.inject.Inject;
import java.util.*;
import static com.juick.util.formatters.PlainTextFormatter.formatPost;
@@ -67,8 +66,6 @@ public class EmailManager implements NotificationListener {
private UserService userService;
@Inject
private ObjectMapper jsonMapper;
- @Inject
- private WebApp webApp;
@Value("${web_domain:localhost}")
private String webDomain;
@Value("${service_email:}")
diff --git a/src/main/java/com/juick/PremiumManager.java b/src/main/java/com/juick/PremiumManager.java
new file mode 100644
index 00000000..f1fd553e
--- /dev/null
+++ b/src/main/java/com/juick/PremiumManager.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+import com.juick.service.EmailService;
+import com.juick.service.PatreonService;
+import com.juick.service.VKService;
+import com.juick.service.security.entities.JuickUser;
+import jakarta.inject.Inject;
+import org.springframework.context.ApplicationListener;
+import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
+
+public class PremiumManager implements ApplicationListener<AuthenticationSuccessEvent> {
+ @Inject
+ VKService vkService;
+ @Inject
+ PatreonService patreonService;
+ @Inject
+ EmailService emailService;
+ @Override
+ public void onApplicationEvent(AuthenticationSuccessEvent event) {
+ var juickUser = (JuickUser)event.getAuthentication().getPrincipal();
+ var isDon = vkService.updatePremiumStatus(juickUser.getUser().getUid());
+ if (!isDon) {
+ patreonService.updateStatus(emailService.getEmails(juickUser.getUser().getUid(), false));
+ }
+ }
+}
diff --git a/src/main/java/com/juick/ServerManager.java b/src/main/java/com/juick/ServerManager.java
index 60b6010f..bc2e7a06 100644
--- a/src/main/java/com/juick/ServerManager.java
+++ b/src/main/java/com/juick/ServerManager.java
@@ -31,8 +31,8 @@ import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
+import jakarta.annotation.Nonnull;
+import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
diff --git a/src/main/java/com/juick/TelegramBotManager.java b/src/main/java/com/juick/TelegramBotManager.java
index cf38ab5c..99b8d873 100644
--- a/src/main/java/com/juick/TelegramBotManager.java
+++ b/src/main/java/com/juick/TelegramBotManager.java
@@ -55,9 +55,9 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
-import javax.annotation.Nonnull;
+import jakarta.annotation.Nonnull;
import jakarta.annotation.PostConstruct;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
diff --git a/src/main/java/com/juick/TopManager.java b/src/main/java/com/juick/TopManager.java
index 13e91b34..a6cd3c0d 100644
--- a/src/main/java/com/juick/TopManager.java
+++ b/src/main/java/com/juick/TopManager.java
@@ -20,7 +20,7 @@ package com.juick;
import java.net.URI;
import java.util.List;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
diff --git a/src/main/java/com/juick/TwitterManager.java b/src/main/java/com/juick/TwitterManager.java
index 7a01debd..10434e3a 100644
--- a/src/main/java/com/juick/TwitterManager.java
+++ b/src/main/java/com/juick/TwitterManager.java
@@ -26,7 +26,7 @@ import com.juick.util.TagUtils;
import com.juick.www.api.SystemActivity;
import org.apache.commons.lang3.StringUtils;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
/**
* @author Ugnich Anton
diff --git a/src/main/java/com/juick/XMPPManager.java b/src/main/java/com/juick/XMPPManager.java
index 9e24c347..ab071260 100644
--- a/src/main/java/com/juick/XMPPManager.java
+++ b/src/main/java/com/juick/XMPPManager.java
@@ -65,7 +65,7 @@ import rocks.xmpp.extensions.vcard.temp.model.VCard;
import rocks.xmpp.extensions.version.SoftwareVersionManager;
import rocks.xmpp.extensions.version.model.SoftwareVersion;
-import javax.annotation.Nonnull;
+import jakarta.annotation.Nonnull;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
diff --git a/src/main/java/com/juick/config/ActivityPubConfig.java b/src/main/java/com/juick/config/ActivityPubConfig.java
index 81327a02..264d59e1 100644
--- a/src/main/java/com/juick/config/ActivityPubConfig.java
+++ b/src/main/java/com/juick/config/ActivityPubConfig.java
@@ -27,7 +27,7 @@ import com.juick.www.api.activity.helpers.ProfileUriBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
@Configuration
public class ActivityPubConfig {
diff --git a/src/main/java/com/juick/config/AppConfig.java b/src/main/java/com/juick/config/AppConfig.java
index cb99c079..a48c7ee0 100644
--- a/src/main/java/com/juick/config/AppConfig.java
+++ b/src/main/java/com/juick/config/AppConfig.java
@@ -20,15 +20,8 @@ package com.juick.config;
import com.juick.*;
import com.juick.model.User;
import com.juick.service.*;
-import com.mitchellbosecke.pebble.extension.FormatterExtension;
import com.overzealous.remark.Options;
import com.overzealous.remark.Remark;
-import io.pebbletemplates.pebble.PebbleEngine;
-import io.pebbletemplates.pebble.loader.ClasspathLoader;
-import io.pebbletemplates.pebble.loader.Loader;
-import io.pebbletemplates.spring.extension.SpringExtension;
-import io.pebbletemplates.spring.servlet.PebbleViewResolver;
-
import org.commonmark.ext.autolink.AutolinkExtension;
import org.commonmark.node.Link;
import org.commonmark.parser.Parser;
@@ -40,13 +33,11 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.security.web.firewall.HttpStatusRequestRejectedHandler;
import org.springframework.security.web.firewall.RequestRejectedHandler;
-import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
import org.springframework.web.servlet.resource.ResourceUrlProvider;
-import java.nio.charset.StandardCharsets;
import java.util.Collections;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
/**
* Created by aalexeev on 11/22/16.
@@ -161,4 +152,5 @@ public class AppConfig {
WatchdogService watchdogService() {
return new SystemdWatchdogService();
}
+
}
diff --git a/src/main/java/com/juick/config/HttpClientConfig.java b/src/main/java/com/juick/config/HttpClientConfig.java
index d838575d..fd94ae4f 100644
--- a/src/main/java/com/juick/config/HttpClientConfig.java
+++ b/src/main/java/com/juick/config/HttpClientConfig.java
@@ -18,7 +18,6 @@
package com.juick.config;
import com.juick.util.ActivityPubRequestInterceptor;
-import okhttp3.Dispatcher;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import org.slf4j.Logger;
@@ -27,7 +26,7 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
@Configuration
public class HttpClientConfig {
diff --git a/src/main/java/com/juick/config/SecurityConfig.java b/src/main/java/com/juick/config/SecurityConfig.java
index a7007648..a93a4a5c 100644
--- a/src/main/java/com/juick/config/SecurityConfig.java
+++ b/src/main/java/com/juick/config/SecurityConfig.java
@@ -62,7 +62,7 @@ import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
@@ -202,13 +202,10 @@ public class SecurityConfig {
"/api/messages/discussions",
"/api/users", "/api/thread", "/api/tags",
"/api/tlgmbtwbhk", "/api/fbwbhk", "/api/_patreon", "/api/_vk",
- "/api/skypebotendpoint", "/api/_fblogin",
- "/api/_vklogin", "/api/_tglogin",
- "/api/_google", "/api/_applelogin", "/api/signup",
+ "/api/skypebotendpoint", "/api/signup",
"/api/inbox", "/api/events", "/api/u/", "/u/**",
"/n/**",
"/api/info/**", "/api/v1/apps", "/api/v1/instance",
- "/api/v2/instance",
"/api/nodeinfo/2.0", "/oauth/**")
.permitAll()
.anyRequest().hasAnyAuthority("SCOPE_write", "ROLE_USER"))
diff --git a/src/main/java/com/juick/config/WebConfig.java b/src/main/java/com/juick/config/WebConfig.java
index a5545c48..35436118 100644
--- a/src/main/java/com/juick/config/WebConfig.java
+++ b/src/main/java/com/juick/config/WebConfig.java
@@ -23,7 +23,7 @@ import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.TimeUnit;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import com.juick.service.UserService;
import com.juick.www.VisitorArgumentResolver;
diff --git a/src/main/java/com/juick/config/XMPPConfig.java b/src/main/java/com/juick/config/XMPPConfig.java
index 2086f237..345f9b3e 100644
--- a/src/main/java/com/juick/config/XMPPConfig.java
+++ b/src/main/java/com/juick/config/XMPPConfig.java
@@ -28,7 +28,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.util.concurrent.Executor;
@Configuration
diff --git a/src/main/java/com/juick/data/MessagesRepository.java b/src/main/java/com/juick/data/MessagesRepository.java
new file mode 100644
index 00000000..7f42c454
--- /dev/null
+++ b/src/main/java/com/juick/data/MessagesRepository.java
@@ -0,0 +1,24 @@
+/*
+ * 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.data;
+
+import com.juick.data.entities.MessageEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface MessagesRepository extends JpaRepository<MessageEntity, Integer> {
+}
diff --git a/src/main/java/com/juick/data/UserMapper.java b/src/main/java/com/juick/data/UserMapper.java
new file mode 100644
index 00000000..ad544116
--- /dev/null
+++ b/src/main/java/com/juick/data/UserMapper.java
@@ -0,0 +1,42 @@
+/*
+ * 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.data;
+
+import com.juick.data.entities.UserEntity;
+import com.juick.model.User;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Named;
+
+import java.time.Instant;
+import java.time.OffsetDateTime;
+
+@Mapper(componentModel = "spring")
+public interface UserMapper {
+ @Mapping(source = "id", target = "uid")
+ @Mapping(source = "seen", target = "seen", qualifiedByName = "offsetDateTimeConverter")
+ User userEntityToUser(UserEntity entity);
+
+ @Named("offsetDateTimeConverter")
+ static Instant map(OffsetDateTime offsetDateTime) {
+ if (offsetDateTime != null) {
+ return offsetDateTime.toInstant();
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/juick/data/UsersRepository.java b/src/main/java/com/juick/data/UsersRepository.java
new file mode 100644
index 00000000..d391d52d
--- /dev/null
+++ b/src/main/java/com/juick/data/UsersRepository.java
@@ -0,0 +1,39 @@
+/*
+ * 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.data;
+
+import com.juick.data.entities.UserEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+@Repository
+public interface UsersRepository extends JpaRepository<UserEntity, Integer> {
+ UserEntity findByName(String name);
+ Optional<UserEntity> findByNameAndPassword(String name, String password);
+ Optional<UserEntity> findByEmailsEmail(String email);
+ Stream<UserEntity> findAllByNameIn(Collection<String> names);
+ Stream<UserEntity> findAllByIdIn(Collection<Integer> uids);
+ Optional<UserEntity> findByFacebookIds_FacebookId(Long facebookId);
+ Optional<UserEntity> findByVkIds_VkId(Long vkId);
+ Optional<UserEntity> findByLogins_Hash(String hash);
+ Optional<UserEntity> findByTgIds_Identifier(Long telegramId);
+}
diff --git a/src/main/java/com/cliqset/xrd/package-info.java b/src/main/java/com/juick/data/entities/BaseMessagePropertyEntity.java
index dfc11f51..90c26dbb 100644
--- a/src/main/java/com/cliqset/xrd/package-info.java
+++ b/src/main/java/com/juick/data/entities/BaseMessagePropertyEntity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2017, Juick
+ * 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
@@ -15,19 +15,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-@XmlSchema(
- namespace=XRD_NAMESPACE,
- elementFormDefault = XmlNsForm.QUALIFIED,
- xmlns={
- @XmlNs(prefix= StringUtils.EMPTY, namespaceURI=XRD_NAMESPACE)
- }
-)
-package com.cliqset.xrd;
+package com.juick.data.entities;
-import org.apache.commons.lang3.StringUtils;
+import jakarta.persistence.*;
-import jakarta.xml.bind.annotation.XmlNs;
-import jakarta.xml.bind.annotation.XmlNsForm;
-import jakarta.xml.bind.annotation.XmlSchema;
+@MappedSuperclass
+public class BaseMessagePropertyEntity {
+ @ManyToOne
+ @JoinColumn(name = "message_id")
+ private MessageEntity message;
+ public MessageEntity getMessage() {
+ return message;
+ }
-import static com.cliqset.xrd.XRDConstants.XRD_NAMESPACE; \ No newline at end of file
+ public void setMessage(MessageEntity message) {
+ this.message = message;
+ }
+}
diff --git a/src/main/java/com/juick/data/entities/BaseUserPropertyEntity.java b/src/main/java/com/juick/data/entities/BaseUserPropertyEntity.java
new file mode 100644
index 00000000..0397c409
--- /dev/null
+++ b/src/main/java/com/juick/data/entities/BaseUserPropertyEntity.java
@@ -0,0 +1,34 @@
+/*
+ * 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.data.entities;
+
+import jakarta.persistence.*;
+
+@MappedSuperclass
+public class BaseUserPropertyEntity {
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id")
+ private UserEntity user;
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public void setUser(UserEntity user) {
+ this.user = user;
+ }
+}
diff --git a/src/main/java/com/juick/data/entities/EmailEntity.java b/src/main/java/com/juick/data/entities/EmailEntity.java
new file mode 100644
index 00000000..10c14c1e
--- /dev/null
+++ b/src/main/java/com/juick/data/entities/EmailEntity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.data.entities;
+
+import jakarta.persistence.*;
+
+@Entity
+@Table(name = "emails")
+public class EmailEntity extends BaseUserPropertyEntity {
+ @Id
+ @Column(name = "email")
+ private String email;
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+}
diff --git a/src/main/java/com/juick/data/entities/FacebookEntity.java b/src/main/java/com/juick/data/entities/FacebookEntity.java
new file mode 100644
index 00000000..6c6b4557
--- /dev/null
+++ b/src/main/java/com/juick/data/entities/FacebookEntity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.data.entities;
+
+import jakarta.persistence.*;
+
+@Entity
+@Table(name = "facebook")
+public class FacebookEntity extends BaseUserPropertyEntity {
+ @Id
+ @Column(name = "fb_id")
+ private Long facebookId;
+
+ public Long getFacebookId() {
+ return facebookId;
+ }
+
+ public void setFacebookId(Long facebookId) {
+ this.facebookId = facebookId;
+ }
+}
diff --git a/src/main/java/com/juick/data/entities/LoginEntity.java b/src/main/java/com/juick/data/entities/LoginEntity.java
new file mode 100644
index 00000000..69737b5d
--- /dev/null
+++ b/src/main/java/com/juick/data/entities/LoginEntity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.data.entities;
+
+import jakarta.persistence.*;
+
+@Entity
+@Table(name = "logins")
+public class LoginEntity extends BaseUserPropertyEntity {
+ @Id
+ @Column(name = "hash", columnDefinition = "char(16)")
+ private String hash;
+
+ public String getHash() {
+ return hash;
+ }
+
+ public void setHash(String hash) {
+ this.hash = hash;
+ }
+}
diff --git a/src/main/java/com/juick/data/entities/MessageEntity.java b/src/main/java/com/juick/data/entities/MessageEntity.java
new file mode 100644
index 00000000..21896fb9
--- /dev/null
+++ b/src/main/java/com/juick/data/entities/MessageEntity.java
@@ -0,0 +1,99 @@
+/*
+ * 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.data.entities;
+
+import jakarta.persistence.*;
+import java.io.Serializable;
+import java.time.Instant;
+import java.util.HashSet;
+import java.util.Set;
+
+@Entity
+@Table(name = "messages")
+public class MessageEntity implements Serializable {
+ @Id
+ @Column(name = "message_id")
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Integer id;
+ @ManyToMany
+ @JoinTable(name = "messages_tags",
+ joinColumns = @JoinColumn(name = "message_id"),
+ inverseJoinColumns = @JoinColumn(name = "tag_id")
+ )
+ private Set<TagEntity> tags = new HashSet<>();
+
+ @OneToMany(mappedBy = "message")
+ private Set<ReplyEntity> replies = new HashSet<>();
+
+ @ManyToOne(cascade = CascadeType.MERGE)
+ private UserEntity user;
+
+ @Column(name = "ts")
+ private Instant ts;
+
+ @Column(name = "txt")
+ private String text;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public Set<TagEntity> getTags() {
+ return tags;
+ }
+
+ public void setTags(Set<TagEntity> tags) {
+ this.tags = tags;
+ }
+
+ public Set<ReplyEntity> getReplies() {
+ return replies;
+ }
+
+ public void setReplies(Set<ReplyEntity> replies) {
+ this.replies = replies;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public void setUser(UserEntity user) {
+ this.user = user;
+ }
+
+ public Instant getTimestamp() {
+ return ts;
+ }
+
+ public void setTimestamp(Instant ts) {
+ this.ts = ts;
+ }
+}
diff --git a/src/main/java/com/juick/data/entities/ReplyEntity.java b/src/main/java/com/juick/data/entities/ReplyEntity.java
new file mode 100644
index 00000000..bc0795cf
--- /dev/null
+++ b/src/main/java/com/juick/data/entities/ReplyEntity.java
@@ -0,0 +1,57 @@
+/*
+ * 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.data.entities;
+
+import jakarta.persistence.*;
+
+@Entity
+@Table(name = "replies")
+public class ReplyEntity extends BaseMessagePropertyEntity {
+ @Id
+ @Column(name = "reply_id")
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long replyId;
+ @Column(name = "txt")
+ private String text;
+ @ManyToOne(cascade = CascadeType.MERGE)
+ private UserEntity user;
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public Long getReplyId() {
+ return replyId;
+ }
+
+ public void setReplyId(Long replyId) {
+ this.replyId = replyId;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public void setUser(UserEntity user) {
+ this.user = user;
+ }
+}
diff --git a/src/main/java/com/juick/data/entities/TagEntity.java b/src/main/java/com/juick/data/entities/TagEntity.java
new file mode 100644
index 00000000..e5c5266d
--- /dev/null
+++ b/src/main/java/com/juick/data/entities/TagEntity.java
@@ -0,0 +1,60 @@
+/*
+ * 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.data.entities;
+
+import jakarta.persistence.*;
+
+import java.util.Set;
+
+@Entity
+@Table(name = "tags")
+public class TagEntity {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "tag_id")
+ private int id;
+ @Column(name = "name")
+ private String name;
+
+ @ManyToMany(mappedBy = "tags")
+ private Set<MessageEntity> messages;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Set<MessageEntity> getMessages() {
+ return messages;
+ }
+
+ public void setMessages(Set<MessageEntity> messages) {
+ this.messages = messages;
+ }
+}
diff --git a/src/main/java/com/juick/data/entities/TelegramEntity.java b/src/main/java/com/juick/data/entities/TelegramEntity.java
new file mode 100644
index 00000000..ccb21022
--- /dev/null
+++ b/src/main/java/com/juick/data/entities/TelegramEntity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.data.entities;
+
+import jakarta.persistence.*;
+
+@Entity
+@Table(name = "telegram")
+public class TelegramEntity extends BaseUserPropertyEntity {
+ @Id
+ @Column(name = "tg_id")
+ private Long identifier;
+
+ public Long getIdentifier() {
+ return identifier;
+ }
+
+ public void setIdentifier(Long identifier) {
+ this.identifier = identifier;
+ }
+}
diff --git a/src/main/java/com/juick/data/entities/UserEntity.java b/src/main/java/com/juick/data/entities/UserEntity.java
new file mode 100644
index 00000000..63edadd9
--- /dev/null
+++ b/src/main/java/com/juick/data/entities/UserEntity.java
@@ -0,0 +1,148 @@
+/*
+ * 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.data.entities;
+
+import jakarta.persistence.*;
+
+import java.time.OffsetDateTime;
+import java.util.HashSet;
+import java.util.Set;
+
+@Entity
+@Table(name = "users")
+public class UserEntity {
+ @Id
+ @Column(name = "id")
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Integer id;
+
+ @Column(name = "nick")
+ private String name;
+
+ @Column(name = "passw")
+ private String password;
+
+ @Column(name = "banned")
+ private boolean banned;
+ @Column(name = "last_seen")
+ private OffsetDateTime seen;
+ @Column
+ private boolean premium;
+
+ @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
+ private Set<EmailEntity> emails = new HashSet<>();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
+ private Set<FacebookEntity> facebookIds = new HashSet<>();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
+ private Set<VKEntity> vkIds = new HashSet<>();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
+ private Set<LoginEntity> logins = new HashSet<>();
+ @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
+ private Set<TelegramEntity> tgIds = new HashSet<>();
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer uid) {
+ this.id = uid;
+ }
+
+ public boolean isBanned() {
+ return banned;
+ }
+
+ public void setBanned(boolean banned) {
+ this.banned = banned;
+ }
+
+ public OffsetDateTime getSeen() {
+ return this.seen;
+ }
+
+ public void setSeen(OffsetDateTime seen) {
+ this.seen = seen;
+ }
+
+ public Set<EmailEntity> getEmails() {
+ return emails;
+ }
+
+ public void setEmails(Set<EmailEntity> emails) {
+ this.emails = emails;
+ }
+
+ public Set<FacebookEntity> getFacebookIds() {
+ return facebookIds;
+ }
+
+ public void setFacebookIds(Set<FacebookEntity> facebookIds) {
+ this.facebookIds = facebookIds;
+ }
+
+ public Set<VKEntity> getVkIds() {
+ return vkIds;
+ }
+
+ public void setVkIds(Set<VKEntity> vkIds) {
+ this.vkIds = vkIds;
+ }
+
+ public Set<LoginEntity> getLogins() {
+ return logins;
+ }
+
+ public void setLogins(Set<LoginEntity> logins) {
+ this.logins = logins;
+ }
+
+ public Set<TelegramEntity> getTgIds() {
+ return tgIds;
+ }
+
+ public void setTgIds(Set<TelegramEntity> tgIds) {
+ this.tgIds = tgIds;
+ }
+
+ public boolean isPremium() {
+ return premium;
+ }
+
+ public void setPremium(boolean premium) {
+ this.premium = premium;
+ }
+}
diff --git a/src/main/java/com/juick/data/entities/VKEntity.java b/src/main/java/com/juick/data/entities/VKEntity.java
new file mode 100644
index 00000000..17840377
--- /dev/null
+++ b/src/main/java/com/juick/data/entities/VKEntity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.data.entities;
+
+import jakarta.persistence.*;
+
+@Entity
+@Table(name = "vk")
+public class VKEntity extends BaseUserPropertyEntity {
+ @Id
+ @Column(name = "vk_id")
+ private Long vkId;
+
+ public Long getVkId() {
+ return vkId;
+ }
+
+ public void setVkId(Long vkId) {
+ this.vkId = vkId;
+ }
+}
diff --git a/src/main/java/com/juick/model/User.java b/src/main/java/com/juick/model/User.java
index 7f04a744..dd0feda1 100644
--- a/src/main/java/com/juick/model/User.java
+++ b/src/main/java/com/juick/model/User.java
@@ -27,7 +27,7 @@ import jakarta.xml.bind.annotation.XmlTransient;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
-import javax.annotation.Nonnull;
+import jakarta.annotation.Nonnull;
import java.io.Serializable;
import java.net.URI;
import java.time.Instant;
@@ -64,6 +64,7 @@ public class User implements Serializable {
private boolean premium;
@XmlTransient
private boolean admin;
+ private String language;
public User() {
tokens = new ArrayList<>();
@@ -241,7 +242,7 @@ public class User implements Serializable {
@XmlTransient
public boolean isVerified() {
- return verified;
+ return verified || !uri.toString().isEmpty();
}
public void setVerified(boolean verified) {
@@ -298,4 +299,12 @@ public class User implements Serializable {
public void setAdmin(boolean admin) {
this.admin = admin;
}
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
}
diff --git a/src/main/java/com/juick/service/ActivityPubService.java b/src/main/java/com/juick/service/ActivityPubService.java
index 75a3b488..a63de1b6 100644
--- a/src/main/java/com/juick/service/ActivityPubService.java
+++ b/src/main/java/com/juick/service/ActivityPubService.java
@@ -41,8 +41,8 @@ import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.tomitribe.auth.signatures.Base64;
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
+import jakarta.annotation.Nonnull;
+import jakarta.inject.Inject;
import java.io.IOException;
import java.net.URI;
import java.security.MessageDigest;
diff --git a/src/main/java/com/juick/service/BaseJdbcService.java b/src/main/java/com/juick/service/BaseJdbcService.java
index b51cbdc7..415efa89 100644
--- a/src/main/java/com/juick/service/BaseJdbcService.java
+++ b/src/main/java/com/juick/service/BaseJdbcService.java
@@ -21,7 +21,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
diff --git a/src/main/java/com/juick/service/EmailService.java b/src/main/java/com/juick/service/EmailService.java
index 614cf412..7d8126c5 100644
--- a/src/main/java/com/juick/service/EmailService.java
+++ b/src/main/java/com/juick/service/EmailService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2023, Juick
+ * 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
@@ -39,4 +39,5 @@ public interface EmailService {
String getEmailByAuthCode(String code);
void deleteAuthCode(String code);
Integer cleanupAuthCodes();
+ boolean isValidEmail(String email);
}
diff --git a/src/main/java/com/juick/service/EmailServiceImpl.java b/src/main/java/com/juick/service/EmailServiceImpl.java
index 383dbdaf..08aaf1b9 100644
--- a/src/main/java/com/juick/service/EmailServiceImpl.java
+++ b/src/main/java/com/juick/service/EmailServiceImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2020, Juick
+ * 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
@@ -17,13 +17,21 @@
package com.juick.service;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.juick.model.User;
+import jakarta.inject.Inject;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
+import java.io.IOException;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
@@ -36,6 +44,13 @@ import java.util.List;
@Transactional
public class EmailServiceImpl extends BaseJdbcService implements EmailService {
+ @Inject
+ private OkHttpClient httpClient;
+ @Inject
+ private ObjectMapper jsonMapper;
+
+ @Value("${email_validation_service_url:http://localhost:8080}")
+ private String emailValidationServiceUrl;
@Override
public boolean verifyAddressByCode(Integer userId, String code) {
try {
@@ -120,4 +135,23 @@ public class EmailServiceImpl extends BaseJdbcService implements EmailService {
new MapSqlParameterSource()
.addValue("day", toDateTime(day.atOffset(ZoneOffset.UTC)), dateTimeType()));
}
+
+ @Override
+ public boolean isValidEmail(String email) {
+ var request = new Request.Builder()
+ .url(emailValidationServiceUrl + "/?email=" + email)
+ .addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
+ .build();
+ try {
+ try(var response = httpClient.newCall(request).execute()) {
+ if (response.isSuccessful() && response.body() != null) {
+ var account = jsonMapper.readTree(response.body().string());
+ return account.has("disposable") && account.get("disposable").textValue().equals("false");
+ }
+ }
+ } catch (IOException e) {
+ return false;
+ }
+ return false;
+ }
}
diff --git a/src/main/java/com/juick/service/InfoService.java b/src/main/java/com/juick/service/InfoService.java
index 9540243e..f6b3cfe1 100644
--- a/src/main/java/com/juick/service/InfoService.java
+++ b/src/main/java/com/juick/service/InfoService.java
@@ -23,7 +23,7 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
diff --git a/src/main/java/com/juick/service/MessagesServiceImpl.java b/src/main/java/com/juick/service/MessagesServiceImpl.java
index 940cdaba..de342977 100644
--- a/src/main/java/com/juick/service/MessagesServiceImpl.java
+++ b/src/main/java/com/juick/service/MessagesServiceImpl.java
@@ -38,13 +38,11 @@ import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
-import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
-import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
@@ -522,7 +520,7 @@ public class MessagesServiceImpl extends BaseJdbcService implements MessagesServ
user.setUri(uri);
String path = uri.getPath();
String name = path.substring(path.lastIndexOf('/') + 1);
- user.setName(name + "@" + uri.getHost());
+ user.setName(name);
}
return new ImmutablePair<>(rs.getInt(1), user);
});
diff --git a/src/main/java/com/juick/service/PatreonService.java b/src/main/java/com/juick/service/PatreonService.java
index fdd6d3dd..8f21f389 100644
--- a/src/main/java/com/juick/service/PatreonService.java
+++ b/src/main/java/com/juick/service/PatreonService.java
@@ -20,20 +20,27 @@ package com.juick.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.OkHttpClient;
import okhttp3.Request;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.stream.StreamSupport;
@Component
public class PatreonService {
+ private static final Logger logger = LoggerFactory.getLogger("Patreon");
+
+ @Inject
+ private UserService userService;
@Inject
private OkHttpClient httpClient;
@Inject
@@ -89,4 +96,30 @@ public class PatreonService {
}
return List.of();
}
+
+ public void updateStatus(List<String> updatedEmails) {
+ var campainsResponse = fetchCampaigns();
+ List<String> activeEmails = new ArrayList<>();
+ campainsResponse.forEach(campaign -> {
+ var pledgesResponse = fetchPledges(campaign);
+ pledgesResponse.forEach(pledge -> {
+ logger.debug("Pledge email: {}", pledge);
+ activeEmails.add(pledge);
+ });
+ });
+ activeEmails.forEach(email -> {
+ var user = userService.getUserByEmail(email);
+ if (!user.isAnonymous()) {
+ userService.setPremium(user.getUid(), true);
+ }
+ });
+ updatedEmails.stream().filter(email -> !activeEmails.contains(email))
+ .forEach(deleted -> {
+ var user = userService.getUserByEmail(deleted);
+ if (!user.isAnonymous() && user.isPremium()) {
+ logger.debug("User is not a patron anymore: {}", deleted);
+ userService.setPremium(user.getUid(), false);
+ }
+ });
+ }
}
diff --git a/src/main/java/com/juick/service/PostgresSearchService.java b/src/main/java/com/juick/service/PostgresSearchService.java
index a243b3f3..2becf64c 100644
--- a/src/main/java/com/juick/service/PostgresSearchService.java
+++ b/src/main/java/com/juick/service/PostgresSearchService.java
@@ -25,7 +25,7 @@ import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Collections;
diff --git a/src/main/java/com/juick/service/SocialService.java b/src/main/java/com/juick/service/SocialService.java
index 2dbef8e5..fa280555 100644
--- a/src/main/java/com/juick/service/SocialService.java
+++ b/src/main/java/com/juick/service/SocialService.java
@@ -19,7 +19,7 @@ package com.juick.service;
import com.juick.model.User;
-import javax.annotation.Nonnull;
+import jakarta.annotation.Nonnull;
import java.util.List;
public interface SocialService {
diff --git a/src/main/java/com/juick/service/SphinxSearchService.java b/src/main/java/com/juick/service/SphinxSearchService.java
index 8a608b44..4171b7f3 100644
--- a/src/main/java/com/juick/service/SphinxSearchService.java
+++ b/src/main/java/com/juick/service/SphinxSearchService.java
@@ -24,7 +24,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
diff --git a/src/main/java/com/juick/service/SubscriptionServiceImpl.java b/src/main/java/com/juick/service/SubscriptionServiceImpl.java
index 4aa5b5ac..057490f8 100644
--- a/src/main/java/com/juick/service/SubscriptionServiceImpl.java
+++ b/src/main/java/com/juick/service/SubscriptionServiceImpl.java
@@ -30,8 +30,8 @@ import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
-import javax.annotation.Nonnull;
-import javax.inject.Inject;
+import jakarta.annotation.Nonnull;
+import jakarta.inject.Inject;
import java.util.Collections;
import java.util.List;
import java.util.Set;
diff --git a/src/main/java/com/juick/service/TagServiceImpl.java b/src/main/java/com/juick/service/TagServiceImpl.java
index 9f5c9356..1eea6812 100644
--- a/src/main/java/com/juick/service/TagServiceImpl.java
+++ b/src/main/java/com/juick/service/TagServiceImpl.java
@@ -27,15 +27,12 @@ import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
-import org.springframework.jdbc.support.GeneratedKeyHolder;
-import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
-import java.sql.Statement;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
diff --git a/src/main/java/com/juick/service/TelegramService.java b/src/main/java/com/juick/service/TelegramService.java
index 16ba531d..71c9b9e3 100644
--- a/src/main/java/com/juick/service/TelegramService.java
+++ b/src/main/java/com/juick/service/TelegramService.java
@@ -18,6 +18,7 @@
package com.juick.service;
import com.juick.model.User;
+import org.springframework.cache.annotation.CacheEvict;
import java.util.List;
@@ -32,6 +33,7 @@ public interface TelegramService {
boolean createTelegramUser(long tgID, String tgName);
+ @CacheEvict(value = "users_by_name", allEntries = true)
boolean deleteTelegramUser(Integer uid);
List<Long> getTelegramIdentifiers(List<User> users);
diff --git a/src/main/java/com/juick/service/TwitterService.java b/src/main/java/com/juick/service/TwitterService.java
index d0c105f2..41e73876 100644
--- a/src/main/java/com/juick/service/TwitterService.java
+++ b/src/main/java/com/juick/service/TwitterService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2023, Juick
+ * 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
@@ -17,7 +17,6 @@
package com.juick.service;
-import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.scribejava.apis.TwitterApi20;
import com.github.scribejava.core.builder.ServiceBuilder;
@@ -35,7 +34,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
@@ -67,7 +66,7 @@ public class TwitterService {
userService.getTwitterToken(jmsg.getUser().getUid()).ifPresent(t -> {
String status = MessageUtils.getMessageHashTags(jmsg) + StringUtils.defaultString(jmsg.getText());
if (status.length() > 253) {
- status = status.substring(0, 252) + "…";
+ status = StringUtils.abbreviate(status, "…", 252);
}
status += " http://juick.com/" + jmsg.getMid();
try {
diff --git a/src/main/java/com/juick/service/UserService.java b/src/main/java/com/juick/service/UserService.java
index 6f7cb58f..845028f7 100644
--- a/src/main/java/com/juick/service/UserService.java
+++ b/src/main/java/com/juick/service/UserService.java
@@ -32,7 +32,6 @@ import org.springframework.cache.annotation.Cacheable;
import java.util.Collection;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
/**
@@ -83,6 +82,8 @@ public interface UserService {
boolean updateUserInfo(User info);
+ boolean updateLanguage(User user);
+
boolean isInWL(int uid, int check);
boolean isInBL(int uid, int check);
@@ -170,7 +171,7 @@ public interface UserService {
boolean updateFacebookUser(long fbID, String token, String fbName);
- int getUIDbyVKID(long vkID);
+ Optional<User> getUserByVKID(long vkID);
boolean createVKUser(long vkID, String loginhash, String token, String vkName, String vkLink);
diff --git a/src/main/java/com/juick/service/UserServiceImpl.java b/src/main/java/com/juick/service/UserServiceImpl.java
index 0bbeea91..bad1378a 100644
--- a/src/main/java/com/juick/service/UserServiceImpl.java
+++ b/src/main/java/com/juick/service/UserServiceImpl.java
@@ -27,16 +27,10 @@ import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.EmptyResultDataAccessException;
-import org.springframework.jdbc.UncategorizedSQLException;
-import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
-import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
-import org.springframework.jdbc.support.GeneratedKeyHolder;
-import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@@ -71,6 +65,7 @@ public class UserServiceImpl extends BaseJdbcService implements UserService {
user.setVerified(rs.getLong(6) > 0);
user.setPremium(rs.getInt(7) > 0);
user.setAdmin(adminUsers.contains(user.getName()));
+ user.setLanguage(rs.getString("lang"));
return user;
}
}
@@ -125,7 +120,7 @@ public class UserServiceImpl extends BaseJdbcService implements UserService {
public Optional<User> getUserByUID(final int uid) {
var list = getJdbcTemplate().query("""
SELECT DISTINCT u.id, u.nick, u.passw, u.banned, u.last_seen,
- COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium
+ COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium, lang
FROM users u LEFT JOIN facebook f ON f.user_id = u.id
LEFT JOIN vk ON u.id = vk.user_id LEFT JOIN telegram t ON u.id = t.user_id
LEFT JOIN emails e ON e.user_id = u.id WHERE u.id = ?""", userMapper, uid);
@@ -139,7 +134,7 @@ public class UserServiceImpl extends BaseJdbcService implements UserService {
if (StringUtils.isNotBlank(username)) {
var list = getJdbcTemplate().query("""
SELECT DISTINCT u.id, u.nick, u.passw, u.banned, u.last_seen,
- COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium
+ COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium, lang
FROM users u LEFT JOIN facebook f ON f.user_id = u.id
LEFT JOIN vk ON u.id = vk.user_id LEFT JOIN telegram t ON u.id = t.user_id
LEFT JOIN emails e ON e.user_id = u.id
@@ -164,7 +159,7 @@ public class UserServiceImpl extends BaseJdbcService implements UserService {
try {
List<User> list = getJdbcTemplate().query(
"SELECT DISTINCT u.id, u.nick, u.passw, u.banned, u.last_seen, " +
- "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium " +
+ "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium, lang " +
"FROM users u LEFT JOIN facebook f ON f.user_id = u.id " +
"LEFT JOIN vk ON u.id = vk.user_id LEFT JOIN telegram t ON u.id = t.user_id " +
"LEFT JOIN emails e ON e.user_id = u.id " +
@@ -190,7 +185,7 @@ public class UserServiceImpl extends BaseJdbcService implements UserService {
if (StringUtils.isNotBlank(jid)) {
List<User> list = getJdbcTemplate().query(
"SELECT DISTINCT u.id, u.nick, u.passw, u.banned, u.last_seen," +
- "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium " +
+ "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium, lang " +
"FROM users u LEFT JOIN facebook f ON f.user_id = u.id " +
"LEFT JOIN vk ON u.id = vk.user_id LEFT JOIN telegram t ON u.id = t.user_id " +
"LEFT JOIN emails e ON e.user_id = u.id " +
@@ -212,7 +207,7 @@ public class UserServiceImpl extends BaseJdbcService implements UserService {
return getNamedParameterJdbcTemplate().query(
"SELECT DISTINCT u.id, u.nick, u.passw, u.banned, u.last_seen," +
- "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium " +
+ "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium, lang " +
"FROM users u LEFT JOIN facebook f ON f.user_id = u.id " +
"LEFT JOIN vk ON u.id = vk.user_id LEFT JOIN telegram t ON u.id = t.user_id " +
"LEFT JOIN emails e ON e.user_id = u.id " +
@@ -229,7 +224,7 @@ public class UserServiceImpl extends BaseJdbcService implements UserService {
return getNamedParameterJdbcTemplate().query(
"SELECT DISTINCT u.id, u.nick, u.passw, u.banned, u.last_seen," +
- "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium " +
+ "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium, lang " +
"FROM users u LEFT JOIN facebook f ON f.user_id = u.id " +
"LEFT JOIN vk ON u.id = vk.user_id LEFT JOIN telegram t ON u.id = t.user_id " +
"LEFT JOIN emails e ON e.user_id = u.id " +
@@ -264,7 +259,7 @@ public class UserServiceImpl extends BaseJdbcService implements UserService {
if (StringUtils.isNotBlank(hash)) {
List<User> list = getJdbcTemplate().query(
"SELECT DISTINCT logins.user_id, u.nick, u.passw, u.banned, u.last_seen," +
- "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium " +
+ "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium, lang " +
"FROM logins INNER JOIN users u ON logins.user_id = u.id " +
"LEFT JOIN facebook f ON f.user_id = u.id " +
"LEFT JOIN vk ON u.id = vk.user_id LEFT JOIN telegram t ON u.id = t.user_id " +
@@ -302,7 +297,7 @@ public class UserServiceImpl extends BaseJdbcService implements UserService {
if (StringUtils.isNotBlank(username)) {
List<User> list = getJdbcTemplate().query(
"SELECT DISTINCT u.id, u.nick, u.passw, u.banned, u.last_seen," +
- "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium " +
+ "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium, lang " +
"FROM users u LEFT JOIN facebook f ON f.user_id = u.id " +
"LEFT JOIN vk ON u.id = vk.user_id LEFT JOIN telegram t ON u.id = t.user_id " +
"LEFT JOIN emails e ON e.user_id = u.id " +
@@ -368,6 +363,15 @@ public class UserServiceImpl extends BaseJdbcService implements UserService {
}
}
+ @Transactional
+ @Override
+ public boolean updateLanguage(final User user) {
+ return getJdbcTemplate().update(
+ "UPDATE users SET lang=? WHERE id=?",
+ user.getLanguage(),
+ user.getUid()) > 0;
+ }
+
@Transactional(readOnly = true)
@Override
public boolean isInWL(final int uid, final int check) {
@@ -758,7 +762,7 @@ public class UserServiceImpl extends BaseJdbcService implements UserService {
List<User> list = getJdbcTemplate().query(
"""
SELECT DISTINCT u.id, u.nick, u.passw, u.banned, u.last_seen,
- COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium
+ COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium, lang
FROM users u LEFT JOIN facebook f ON f.user_id = u.id
LEFT JOIN vk ON u.id = vk.user_id LEFT JOIN telegram t ON u.id = t.user_id
LEFT JOIN emails e ON e.user_id = u.id
@@ -791,7 +795,7 @@ public class UserServiceImpl extends BaseJdbcService implements UserService {
public Optional<User> getUserByFacebookId(long facebookId) {
List<User> list = getJdbcTemplate().query(
"SELECT DISTINCT u.id, u.nick, u.passw, u.banned, u.last_seen, " +
- "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium " +
+ "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium, lang " +
"FROM users u LEFT JOIN facebook f ON f.user_id = u.id " +
"LEFT JOIN vk ON u.id = vk.user_id LEFT JOIN telegram t ON u.id = t.user_id " +
"LEFT JOIN emails e ON e.user_id = u.id WHERE f.fb_id = ?", new UserMapper(), facebookId);
@@ -815,10 +819,15 @@ public class UserServiceImpl extends BaseJdbcService implements UserService {
@Transactional(readOnly = true)
@Override
- public int getUIDbyVKID(long vkID) {
- var users = getJdbcTemplate().queryForList("""
- SELECT user_id FROM vk WHERE vk_id=? AND user_id IS NOT NULL""", Integer.class, vkID);
- return users.isEmpty() ? 0 : users.get(0);
+ public Optional<User> getUserByVKID(long vkID) {
+ List<User> list = getJdbcTemplate().query(
+ "SELECT DISTINCT u.id, u.nick, u.passw, u.banned, u.last_seen, " +
+ "COALESCE(f.fb_id, vk.vk_id, t.tg_id, e.user_id, 0) AS verified, premium, lang " +
+ "FROM users u LEFT JOIN facebook f ON f.user_id = u.id " +
+ "LEFT JOIN vk ON u.id = vk.user_id LEFT JOIN telegram t ON u.id = t.user_id " +
+ "LEFT JOIN emails e ON e.user_id = u.id WHERE vk.vk_id = ?", new UserMapper(), vkID);
+
+ return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0));
}
@Transactional
diff --git a/src/main/java/com/juick/service/VKService.java b/src/main/java/com/juick/service/VKService.java
index 14d7e3e9..7f6b2516 100644
--- a/src/main/java/com/juick/service/VKService.java
+++ b/src/main/java/com/juick/service/VKService.java
@@ -30,8 +30,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
+import org.springframework.web.util.UriComponentsBuilder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
@Service
public class VKService {
@@ -40,7 +41,8 @@ public class VKService {
private String VK_APPID;
@Value("${vk_secret:secret}")
private String VK_SECRET;
- private static final String VK_REDIRECT = "https://juick.com/_vklogin";
+ @Value("${ap_base_uri:http://localhost:8080/}")
+ private String baseUri;
@Inject
private ObjectMapper jsonMapper;
@Inject
@@ -49,14 +51,16 @@ public class VKService {
@PostConstruct
public void init() {
+ UriComponentsBuilder redirectBuilder = UriComponentsBuilder.fromUriString(baseUri);
+ String redirectUri = redirectBuilder.replacePath("/_vklogin").build().toUriString();
ServiceBuilder vkBuilder = new ServiceBuilder(VK_APPID);
setVkAuthService(vkBuilder.apiSecret(VK_SECRET)
.defaultScope("friends,wall,offline,groups")
- .callback(VK_REDIRECT)
+ .callback(redirectUri)
.build(VkontakteApi.instance()));
}
- public void updatePremiumStatus(Integer userId) {
+ public boolean updatePremiumStatus(Integer userId) {
var vkUser = userService.getVkTokens(userId);
if (vkUser != null) {
OAuth2AccessToken token = new OAuth2AccessToken(vkUser.getRight());
@@ -65,12 +69,13 @@ public class VKService {
getVkAuthService().signRequest(token, donRequest);
try (Response vkResponse = getVkAuthService().execute(donRequest)) {
if (vkResponse.isSuccessful()) {
- logger.info(vkResponse.getBody());
+ logger.debug(vkResponse.getBody());
var response = jsonMapper.readTree(vkResponse.getBody());
if (response.has("response")) {
var isDon = response.get("response").intValue() > 0;
- logger.info("{} is Don: {}", vkUser.getLeft(), isDon);
+ logger.debug("{} is Don: {}", vkUser.getLeft(), isDon);
userService.setPremium(userId, isDon);
+ return isDon;
} else {
// token is expired or does not have "groups" permissions
userService.updateVkToken(userId, "");
@@ -80,8 +85,9 @@ public class VKService {
logger.error("Don request error", e);
}
} else {
- logger.warn("User is not connected to VK: {}", userId);
+ logger.debug("User is not connected to VK: {}", userId);
}
+ return false;
}
public OAuth20Service getVkAuthService() {
diff --git a/src/main/java/com/juick/service/WebfingerService.java b/src/main/java/com/juick/service/WebfingerService.java
index 4accfc94..89ffc230 100644
--- a/src/main/java/com/juick/service/WebfingerService.java
+++ b/src/main/java/com/juick/service/WebfingerService.java
@@ -30,7 +30,7 @@ import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import rocks.xmpp.addr.Jid;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.IOException;
import java.net.URI;
diff --git a/src/main/java/com/juick/service/component/SystemEvent.java b/src/main/java/com/juick/service/component/SystemEvent.java
index 830813ad..9acd7a2b 100644
--- a/src/main/java/com/juick/service/component/SystemEvent.java
+++ b/src/main/java/com/juick/service/component/SystemEvent.java
@@ -17,7 +17,7 @@
package com.juick.service.component;
-import javax.annotation.Nonnull;
+import jakarta.annotation.Nonnull;
import com.juick.www.api.SystemActivity;
import org.springframework.context.ApplicationEvent;
diff --git a/src/main/java/com/juick/util/MessageUtils.java b/src/main/java/com/juick/util/MessageUtils.java
index a4407f17..ee6ff772 100644
--- a/src/main/java/com/juick/util/MessageUtils.java
+++ b/src/main/java/com/juick/util/MessageUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2020, Juick
+ * 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
@@ -17,23 +17,17 @@
package com.juick.util;
+import com.juick.model.Entity;
import com.juick.model.Message;
import com.juick.model.Tag;
import com.juick.model.User;
-import com.juick.model.Entity;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -48,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 = ">" + quote.substring(0, 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;
}
}
@@ -63,7 +64,7 @@ public class MessageUtils {
private final static String urlWhiteSpacePrefix = "((?<=\\s)|(?<=\\A))";
- private final static String urlRegex = "((?:ht|f)tps?://(?:www\\.)?([^\\/\\s\\n\\\"]+)/?[^\\]\\s\\n\\\"\\>]*)";
+ private final static String urlRegex = "((?:(?:ht|f)tps?://(?:www\\.)?([^\\s()<>/?#]+)([^\\s()<>«»]*)?))";
private final static String urlWithWhitespacesRegex =
urlWhiteSpacePrefix + urlRegex;
@@ -197,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>
@@ -226,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;
diff --git a/src/main/java/com/juick/util/formatters/PlainTextFormatter.java b/src/main/java/com/juick/util/formatters/PlainTextFormatter.java
index 9a885bca..682a15b4 100644
--- a/src/main/java/com/juick/util/formatters/PlainTextFormatter.java
+++ b/src/main/java/com/juick/util/formatters/PlainTextFormatter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2020, Juick
+ * 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
@@ -62,7 +62,7 @@ public class PlainTextFormatter {
sb.append(attachmentUrl).append("\n");
}
if (txt.length() >= cropLength) {
- sb.append(StringUtils.substring(txt, 0, cropLength)).append(" [...]");
+ sb.append(StringUtils.abbreviate(txt, "[…]", cropLength));
} else {
sb.append(txt);
}
diff --git a/src/main/java/com/juick/util/xmpp/JidConverter.java b/src/main/java/com/juick/util/xmpp/JidConverter.java
index 4f457164..5c40d470 100644
--- a/src/main/java/com/juick/util/xmpp/JidConverter.java
+++ b/src/main/java/com/juick/util/xmpp/JidConverter.java
@@ -18,13 +18,14 @@
package com.juick.util.xmpp;
import org.springframework.core.convert.converter.Converter;
+import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import rocks.xmpp.addr.Jid;
public class JidConverter implements Converter<String, Jid> {
@Nullable
@Override
- public Jid convert(String jidStr) {
+ public Jid convert(@NonNull String jidStr) {
return Jid.of(jidStr);
}
}
diff --git a/src/main/java/com/juick/www/VisitorArgumentResolver.java b/src/main/java/com/juick/www/VisitorArgumentResolver.java
index a092581a..6b85c0cd 100644
--- a/src/main/java/com/juick/www/VisitorArgumentResolver.java
+++ b/src/main/java/com/juick/www/VisitorArgumentResolver.java
@@ -29,7 +29,7 @@ import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
public class VisitorArgumentResolver implements HandlerMethodArgumentResolver {
private final UserService userService;
diff --git a/src/main/java/com/juick/www/WebApp.java b/src/main/java/com/juick/www/WebApp.java
index cf9570aa..e8c562c5 100644
--- a/src/main/java/com/juick/www/WebApp.java
+++ b/src/main/java/com/juick/www/WebApp.java
@@ -26,7 +26,7 @@ import org.springframework.stereotype.Component;
import org.springframework.web.servlet.resource.ResourceUrlProvider;
import org.springframework.web.util.UriComponentsBuilder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
diff --git a/src/main/java/com/juick/www/ad/SapeService.java b/src/main/java/com/juick/www/ad/SapeService.java
index 97ab882b..ca823974 100644
--- a/src/main/java/com/juick/www/ad/SapeService.java
+++ b/src/main/java/com/juick/www/ad/SapeService.java
@@ -31,7 +31,7 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.UriComponents;
import ru.sape.Sape;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.net.URI;
@ControllerAdvice(assignableTypes = Site.class)
diff --git a/src/main/java/com/juick/www/api/ApiSocialLogin.java b/src/main/java/com/juick/www/api/ApiSocialLogin.java
deleted file mode 100644
index c8758d59..00000000
--- a/src/main/java/com/juick/www/api/ApiSocialLogin.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2008-2020, 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.fasterxml.jackson.databind.ObjectMapper;
-import com.github.scribejava.apis.AppleClientSecretGenerator;
-import com.github.scribejava.apis.AppleSignInApi;
-import com.github.scribejava.apis.FacebookApi;
-import com.github.scribejava.apis.GoogleTokenVerifier;
-import com.github.scribejava.apis.VkontakteApi;
-import com.github.scribejava.core.builder.ServiceBuilder;
-import com.github.scribejava.core.model.OAuth2AccessToken;
-import com.github.scribejava.core.model.OAuthRequest;
-import com.github.scribejava.core.model.Verb;
-import com.github.scribejava.core.oauth.OAuth20Service;
-import com.juick.model.AuthResponse;
-import com.juick.model.ext.facebook.User;
-import com.juick.model.ext.vk.UsersResponse;
-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.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.math.NumberUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.util.UriComponentsBuilder;
-
-import jakarta.annotation.PostConstruct;
-import javax.inject.Inject;
-import java.io.IOException;
-import java.util.Map;
-import java.util.Optional;
-import java.util.UUID;
-import java.util.concurrent.ExecutionException;
-
-/**
- *
- * @author Ugnich Anton
- */
-@Controller
-public class ApiSocialLogin {
-
- private static final Logger logger = LoggerFactory.getLogger(ApiSocialLogin.class);
-
- @Value("${facebook_appid:appid}")
- private String FACEBOOK_APPID;
- @Value("${facebook_secret:secret}")
- private String FACEBOOK_SECRET;
- private static final String FACEBOOK_REDIRECT = "https://api.juick.com/_fblogin";
- private static final String VK_REDIRECT = "https://api.juick.com/_vklogin";
- @Inject
- private ObjectMapper jsonMapper;
- private OAuth20Service facebookAuthService, vkAuthService, appleSignInService;
-
- @Value("${twitter_consumer_key:appid}")
- private String twitterConsumerKey;
- @Value("${twitter_consumer_secret:secret}")
- private String twitterConsumerSecret;
- @Value("${vk_appid:appid}")
- private String VK_APPID;
- @Value("${vk_secret:secret}")
- private String VK_SECRET;
- @Value("${google_client_id:}")
- private String googleClientId;
- @Value("${apple_app_id:appid}")
- private String appleApplicationId;
- @Value("${ap_base_uri:http://localhost:8080/}")
- private String baseUri;
-
- @Inject
- private UserService userService;
- @Inject
- private EmailService emailService;
- @Inject
- private AppleClientSecretGenerator clientSecretGenerator;
- @Inject
- private Users users;
-
- @PostConstruct
- public void init() {
- ServiceBuilder facebookBuilder = new ServiceBuilder(FACEBOOK_APPID);
- ServiceBuilder vkBuilder = new ServiceBuilder(VK_APPID);
- facebookAuthService = facebookBuilder
- .apiSecret(FACEBOOK_SECRET)
- .callback(FACEBOOK_REDIRECT)
- .defaultScope("email")
- .build(FacebookApi.instance());
- vkAuthService = vkBuilder
- .apiSecret(VK_SECRET)
- .defaultScope("friends,wall,offline,groups")
- .callback(VK_REDIRECT)
- .build(VkontakteApi.instance());
- ServiceBuilder appleSignInBuilder = new ServiceBuilder(appleApplicationId);
- UriComponentsBuilder redirectBuilder = UriComponentsBuilder.fromUriString(baseUri);
- String appleSignInRedirectUri = redirectBuilder.replacePath("/api/_applelogin").build().toUriString();
- appleSignInService = appleSignInBuilder
- .callback(appleSignInRedirectUri)
- .defaultScope("email")
- .build(new AppleSignInApi(clientSecretGenerator, appleApplicationId));
- }
-
- @GetMapping("/api/_fblogin")
- protected String doFacebookLogin(@RequestParam(required = false) String code,
- @RequestParam(required = false) String state) throws IOException, ExecutionException, InterruptedException {
- if (StringUtils.isBlank(code)) {
- String fbstate = UUID.randomUUID().toString();
- userService.addFacebookState(fbstate, state);
- return "redirect:" + facebookAuthService.getAuthorizationUrl(fbstate);
- }
-
- String redirectUrl = userService.verifyFacebookState(state);
-
- if (StringUtils.isEmpty(redirectUrl)) {
- logger.error("state is missing");
- throw new HttpBadRequestException();
- }
- OAuth2AccessToken token = facebookAuthService.getAccessToken(code);
- final OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://graph.facebook.com/me?fields=id,name,email");
- facebookAuthService.signRequest(token, meRequest);
- String graph = facebookAuthService.execute(meRequest).getBody();
- if (StringUtils.isBlank(graph)) {
- logger.error("FACEBOOK GRAPH ERROR");
- throw new HttpBadRequestException();
- }
- User fb = jsonMapper.readValue(graph, User.class);
- long fbID = NumberUtils.toLong(fb.id(), 0);
- if (fbID == 0 || StringUtils.isBlank(fb.name())) {
- logger.error("Missing required fields, id: {}, name: {}", fbID, fb.name());
- throw new HttpBadRequestException();
- }
-
- Optional<com.juick.model.User> existingFacebookUser = userService.getUserByFacebookId(fbID);
- if (existingFacebookUser.isPresent()) {
- if (!userService.updateFacebookUser(fbID, token.getAccessToken(), fb.name())) {
- logger.error("error updating facebook user, id: {}, token: {}", fbID, token.getAccessToken());
- throw new HttpBadRequestException();
- }
- if (StringUtils.isNotEmpty(fb.email())) {
- logger.info("found {} for facebook user {}", fb.email(), fb.name());
- Integer userId = existingFacebookUser.get().getUid();
- if (!emailService.getEmails(userId, false).contains(fb.email())) {
- emailService.addEmail(userId, fb.email());
- }
- }
- UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(redirectUrl);
- uriComponentsBuilder.queryParam("hash", userService.getHashByUID(existingFacebookUser.get().getUid()));
- uriComponentsBuilder.queryParam("retpath", redirectUrl);
- return "redirect:" + uriComponentsBuilder.build().toUriString();
- } else {
- if (!userService.createFacebookUser(fbID, state, token.getAccessToken(), fb.name())) {
- throw new HttpBadRequestException();
- }
- return "redirect:/signup?type=fb&hash=" + state;
- }
- }
- @GetMapping("/api/_vklogin")
- protected String doVKLogin(@RequestParam(required = false) String code,
- @RequestParam String state) throws IOException, ExecutionException, InterruptedException {
- if (StringUtils.isBlank(code)) {
- String vkstate = UUID.randomUUID().toString();
- userService.addVKState(vkstate, state);
- return "redirect:" + vkAuthService.getAuthorizationUrl(vkstate);
- }
-
- String redirectUrl = userService.verifyVKState(state);
- if (StringUtils.isBlank(redirectUrl)) {
- logger.error("state is missing");
- throw new HttpBadRequestException();
- }
- OAuth2AccessToken token = vkAuthService.getAccessToken(code);
-
- OAuthRequest meRequest = new OAuthRequest(Verb.GET, "https://api.vk.com/method/users.get?fields=screen_name&v=5.131");
- vkAuthService.signRequest(token, meRequest);
- String graph = vkAuthService.execute(meRequest).getBody();
-
- com.juick.model.ext.vk.User jsonUser = jsonMapper.readValue(graph, UsersResponse.class).users().get(0);
- String vkName = jsonUser.firstName() + " " + jsonUser.lastName();
- String vkLink = jsonUser.screenName();
-
- if (vkName.length() == 1 || StringUtils.isBlank(vkLink)) {
- logger.error("vk user error");
- throw new HttpBadRequestException();
- }
-
- long vkID = NumberUtils.toLong(jsonUser.id(), 0);
- int uid = userService.getUIDbyVKID(vkID);
- if (uid > 0) {
- UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(redirectUrl);
- uriComponentsBuilder.queryParam("hash", userService.getHashByUID(uid));
- uriComponentsBuilder.queryParam("retpath", redirectUrl);
- return "redirect:" + uriComponentsBuilder.build().toUriString();
- } else {
- String loginhash = UUID.randomUUID().toString();
- if (!userService.createVKUser(vkID, loginhash, token.getAccessToken(), vkName, vkLink)) {
- logger.error("create vk user error");
- throw new HttpBadRequestException();
- }
- return "redirect:/signup?type=vk&hash=" + loginhash;
- }
- }
- @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();
- }
- @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.isNotEmpty(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();
- }
- }
- @GetMapping("/api/_applelogin")
- public String doAppleLogin(@RequestParam(required = false) String code, @RequestParam String state) {
- if (StringUtils.isBlank(code)) {
- String astate = UUID.randomUUID().toString();
- userService.addVKState(astate, state);
- return "redirect:" + appleSignInService.getAuthorizationUrl(astate);
- }
- throw new HttpBadRequestException();
- }
- @PostMapping("/api/_applelogin")
- public String doVerifyAppleResponse(@RequestParam Map<String, String> body) throws InterruptedException, ExecutionException, IOException {
- OAuth2AccessToken token = appleSignInService.getAccessToken(body.get("code"));
- var jsonNode = jsonMapper.readTree(token.getRawResponse());
- var idToken = jsonNode.get("id_token").textValue();
- logger.info("Token: {}", idToken);
- AppleSignInApi api = (AppleSignInApi) appleSignInService.getApi();
- var email = api.validateToken(idToken);
-
- if (email.isPresent()) {
- com.juick.model.User user = userService.getUserByEmail(email.get());
- if (!user.isAnonymous()) {
- String redirectUrl = userService.verifyVKState(body.get("state"));
- if (StringUtils.isBlank(redirectUrl)) {
- logger.error("state is missing");
- throw new HttpBadRequestException();
- }
- UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(redirectUrl);
- uriComponentsBuilder.queryParam("hash", userService.getHashByUID(user.getUid()));
- uriComponentsBuilder.queryParam("retpath", redirectUrl);
- return "redirect:" + uriComponentsBuilder.build().toUriString();
- } else {
- String verificationCode = RandomStringUtils.randomAlphanumeric(8).toUpperCase();
- emailService.addVerificationCode(null, email.get(), verificationCode);
- return "redirect:/signup?type=email&hash=" + verificationCode;
- }
- }
- throw new HttpBadRequestException();
- }
-}
diff --git a/src/main/java/com/juick/www/api/Mastodon.java b/src/main/java/com/juick/www/api/Mastodon.java
index 190ee5ef..ca3687e7 100644
--- a/src/main/java/com/juick/www/api/Mastodon.java
+++ b/src/main/java/com/juick/www/api/Mastodon.java
@@ -49,7 +49,7 @@ import org.springframework.security.oauth2.server.authorization.settings.ClientS
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.web.bind.annotation.*;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.net.URI;
import java.time.Duration;
import java.time.Instant;
@@ -164,7 +164,7 @@ public class Mastodon {
) {
}
- @GetMapping({"/api/v1/instance", "/api/v2/instance"})
+ @GetMapping("/api/v1/instance")
public Instance getInstance() {
return new Instance(domain, "Microblogging service", "Juick", "2.x","support@juick.com");
}
diff --git a/src/main/java/com/juick/www/api/Messages.java b/src/main/java/com/juick/www/api/Messages.java
index 969bef4f..ecea513b 100644
--- a/src/main/java/com/juick/www/api/Messages.java
+++ b/src/main/java/com/juick/www/api/Messages.java
@@ -39,7 +39,7 @@ import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.IOException;
import java.util.*;
diff --git a/src/main/java/com/juick/www/api/Notifications.java b/src/main/java/com/juick/www/api/Notifications.java
index c1ee74ab..f8f8dc14 100644
--- a/src/main/java/com/juick/www/api/Notifications.java
+++ b/src/main/java/com/juick/www/api/Notifications.java
@@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
diff --git a/src/main/java/com/juick/www/api/PM.java b/src/main/java/com/juick/www/api/PM.java
index 5f0988c1..64f9bd82 100644
--- a/src/main/java/com/juick/www/api/PM.java
+++ b/src/main/java/com/juick/www/api/PM.java
@@ -34,7 +34,7 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.util.Collections;
import java.util.List;
diff --git a/src/main/java/com/juick/www/api/Post.java b/src/main/java/com/juick/www/api/Post.java
index 4f1c2cad..57c23703 100644
--- a/src/main/java/com/juick/www/api/Post.java
+++ b/src/main/java/com/juick/www/api/Post.java
@@ -22,7 +22,7 @@ import java.net.URL;
import java.util.List;
import java.util.Optional;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import com.juick.www.api.activity.helpers.ProfileUriBuilder;
import io.swagger.v3.oas.annotations.Parameter;
@@ -48,7 +48,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpStatus;
+import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -74,38 +76,36 @@ public class Post {
@RequestMapping(value = "/api/post", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(value = HttpStatus.OK)
- public CommandResult doPostMessage(
+ public ResponseEntity<?> doPostMessage(
@Parameter(hidden = true) User visitor,
@RequestParam(required = false, defaultValue = StringUtils.EMPTY) String body,
@RequestParam(required = false) String img,
@RequestParam(required = false) MultipartFile attach) throws Exception {
body = body.replace("\r", StringUtils.EMPTY);
-
- URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, storageService.getTemporaryDirectory());
-
- if (StringUtils.isBlank(attachmentFName.toString()) && img != null && img.length() > 10) {
- URI juickUri = URI.create(img);
- if (juickUri.getScheme().equals("juick")) {
- attachmentFName = juickUri;
- } else {
- try {
+ try {
+ URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, storageService.getTemporaryDirectory());
+ if (StringUtils.isBlank(attachmentFName.toString()) && img != null && img.length() > 10) {
+ URI juickUri = URI.create(img);
+ if (juickUri.getScheme().equals("juick")) {
+ attachmentFName = juickUri;
+ } else {
URL imgUrl = new URL(img);
attachmentFName = HttpUtils.downloadImage(imgUrl, storageService.getTemporaryDirectory());
- } catch (Exception e) {
- logger.error("DOWNLOAD ERROR", e);
- throw new HttpBadRequestException();
}
}
+ if (StringUtils.isBlank(body) && StringUtils.isBlank(attachmentFName.toString())) {
+ // Should be there for compatibility
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(CommandResult.fromString("Empty message"));
+ }
+ return ResponseEntity.ok(commandsManager.processCommand(visitor, body, attachmentFName));
+ } catch (Exception e) {
+ logger.error("DOWNLOAD ERROR", e);
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(CommandResult.fromString(e.getMessage()));
}
- if (StringUtils.isBlank(body) && StringUtils.isBlank(attachmentFName.toString())) {
- // Should be there for compatibility
- throw new HttpBadRequestException();
- }
- return commandsManager.processCommand(visitor, body, attachmentFName);
}
@RequestMapping(value = "/api/comment", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
- public CommandResult doPostComment(
+ public ResponseEntity<?> doPostComment(
@Parameter(hidden = true) User visitor,
@RequestParam(defaultValue = "0") int mid,
@RequestParam(defaultValue = "0") int rid,
@@ -114,11 +114,11 @@ public class Post {
@RequestParam(required = false) MultipartFile attach)
throws Exception {
if (mid == 0) {
- throw new HttpBadRequestException();
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(CommandResult.fromString("Invalid mid"));
}
Optional<Message> message = messagesService.getMessage(mid);
if (message.isEmpty()) {
- throw new HttpNotFoundException();
+ return ResponseEntity.status(HttpStatus.NOT_FOUND).body(CommandResult.fromString("Message not found"));
}
Message msg = message.get();
@@ -127,7 +127,7 @@ public class Post {
if (rid > 0) {
reply = messagesService.getReply(mid, rid);
if (reply == null) {
- throw new HttpNotFoundException();
+ return ResponseEntity.status(HttpStatus.NOT_FOUND).body(CommandResult.fromString("Reply not found"));
}
}
@@ -135,25 +135,23 @@ public class Post {
|| userService.isInBL(visitor.getUid(), msg.getUser().getUid())
|| (reply != null && userService.isInBL(visitor.getUid(), reply.getUser().getUid()))) {
// TODO: validator
- throw new HttpForbiddenException();
+ return ResponseEntity.status(HttpStatus.FORBIDDEN).body(CommandResult.fromString("Forbidden"));
}
-
- URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, storageService.getTemporaryDirectory());
-
- if (StringUtils.isBlank(attachmentFName.toString()) && img != null && img.length() > 10) {
- try {
+ try {
+ URI attachmentFName = HttpUtils.receiveMultiPartFile(attach, storageService.getTemporaryDirectory());
+ if (StringUtils.isBlank(attachmentFName.toString()) && img != null && img.length() > 10) {
attachmentFName = HttpUtils.downloadImage(new URL(img), storageService.getTemporaryDirectory());
- } catch (Exception e) {
- logger.error("DOWNLOAD ERROR", e);
- throw new HttpBadRequestException();
}
+ if (StringUtils.isBlank(body) && StringUtils.isBlank(attachmentFName.toString())) {
+ // Should be there for compatibility
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(CommandResult.fromString("Empty message"));
+ }
+ return ResponseEntity.ok(commandsManager.processCommand(visitor, String.format("#%d/%d %s", mid, rid, body),
+ attachmentFName));
+ } catch (Exception e) {
+ logger.error("DOWNLOAD ERROR", e);
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(CommandResult.fromString(e.getMessage()));
}
- if (StringUtils.isBlank(body) && StringUtils.isBlank(attachmentFName.toString())) {
- // Should be there for compatibility
- throw new HttpBadRequestException();
- }
- return commandsManager.processCommand(visitor, String.format("#%d/%d %s", mid, rid, body),
- attachmentFName);
}
@PostMapping("/api/like")
diff --git a/src/main/java/com/juick/www/api/Service.java b/src/main/java/com/juick/www/api/Service.java
index 41fdcfbf..ed6699d2 100644
--- a/src/main/java/com/juick/www/api/Service.java
+++ b/src/main/java/com/juick/www/api/Service.java
@@ -37,6 +37,7 @@ import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
+import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
@@ -53,7 +54,7 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -221,7 +222,8 @@ public class Service {
}
@GetMapping("/api/events")
- public SseEmitter handle(@Parameter(hidden = true) User visitor) {
+ public SseEmitter handle(@Parameter(hidden = true) User visitor, HttpServletResponse response) {
+ response.addHeader("X-Accel-Buffering", "no");
logger.info("{} connected", visitor.getName());
if (!visitor.isAnonymous()) {
userService.updateLastSeen(visitor);
diff --git a/src/main/java/com/juick/www/api/SystemActivity.java b/src/main/java/com/juick/www/api/SystemActivity.java
index a75ef3b6..33d4e1d3 100644
--- a/src/main/java/com/juick/www/api/SystemActivity.java
+++ b/src/main/java/com/juick/www/api/SystemActivity.java
@@ -23,7 +23,7 @@ import com.juick.model.User;
import java.util.Collections;
import java.util.List;
-import javax.annotation.Nonnull;
+import jakarta.annotation.Nonnull;
public class SystemActivity {
private SystemActivity(ActivityType type) {
diff --git a/src/main/java/com/juick/www/api/Tags.java b/src/main/java/com/juick/www/api/Tags.java
index 5bb25af6..b89918a6 100644
--- a/src/main/java/com/juick/www/api/Tags.java
+++ b/src/main/java/com/juick/www/api/Tags.java
@@ -24,7 +24,7 @@ import io.swagger.v3.oas.annotations.Parameter;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.util.List;
/**
diff --git a/src/main/java/com/juick/www/api/Users.java b/src/main/java/com/juick/www/api/Users.java
index 34382f8e..923d00b9 100644
--- a/src/main/java/com/juick/www/api/Users.java
+++ b/src/main/java/com/juick/www/api/Users.java
@@ -23,7 +23,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import com.juick.model.AnonymousUser;
import com.juick.model.ApplicationStatus;
diff --git a/src/main/java/com/juick/www/api/activity/Profile.java b/src/main/java/com/juick/www/api/activity/Profile.java
index 101c59f9..e5bd04ac 100644
--- a/src/main/java/com/juick/www/api/activity/Profile.java
+++ b/src/main/java/com/juick/www/api/activity/Profile.java
@@ -52,7 +52,7 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.UriComponentsBuilder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
diff --git a/src/main/java/com/juick/www/api/hostmeta/HostMeta.java b/src/main/java/com/juick/www/api/hostmeta/HostMeta.java
deleted file mode 100644
index c6f1dce6..00000000
--- a/src/main/java/com/juick/www/api/hostmeta/HostMeta.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2008-2020, 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.hostmeta;
-
-import com.cliqset.xrd.Link;
-import com.cliqset.xrd.XRD;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.Collections;
-
-import static com.cliqset.xrd.XRDConstants.XRD_MEDIA_TYPE;
-
-@RestController
-public class HostMeta {
- @Value("${ap_base_uri:http://localhost:8080/}")
- private String baseUri;
- @GetMapping(value = "/.well-known/host-meta", produces = { XRD_MEDIA_TYPE, MediaType.APPLICATION_XML_VALUE })
- public XRD hostMetaResponse() {
- Link webfinger = new Link();
- webfinger.setTemplate(String.format("%swebfinger?resource={uri}", baseUri));
- XRD xrd = new XRD();
- xrd.setLinks(Collections.singletonList(webfinger));
- return xrd;
- }
-}
diff --git a/src/main/java/com/juick/www/api/webfinger/Resource.java b/src/main/java/com/juick/www/api/webfinger/Resource.java
index 4b2bc388..3f04097c 100644
--- a/src/main/java/com/juick/www/api/webfinger/Resource.java
+++ b/src/main/java/com/juick/www/api/webfinger/Resource.java
@@ -17,53 +17,6 @@
package com.juick.www.api.webfinger;
-import com.juick.model.User;
-import com.juick.www.api.webfinger.model.Account;
-import com.juick.www.api.webfinger.model.Link;
-import com.juick.util.HttpNotFoundException;
-import com.juick.service.UserService;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.util.UriComponentsBuilder;
-import rocks.xmpp.addr.Jid;
-
-import javax.inject.Inject;
-import java.util.List;
-
-import static com.juick.www.api.activity.model.Context.ACTIVITY_MEDIA_TYPE;
-
-@RestController
public class Resource {
public static final String MEDIA_TYPE = "application/jrd+json";
- @Inject
- private UserService userService;
- @Value("${web_domain:localhost}")
- private String domain;
- @Value("${ap_base_uri:http://localhost:8080/}")
- private String baseUri;
-
- @GetMapping(value = "/.well-known/webfinger", produces = {
- Resource.MEDIA_TYPE, MediaType.APPLICATION_JSON_VALUE })
- public Account getWebResource(@RequestParam String resource) {
- if (resource.startsWith("acct:")) {
- try {
- Jid account = Jid.of(resource.substring(5));
- if (account.getDomain().equals(domain)) {
- User user = userService.getUserByName(account.getLocal());
- if (!user.isAnonymous()) {
- UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUri);
- builder.path(String.format("/u/%s", user.getName()));
- Link blog = new Link("self", ACTIVITY_MEDIA_TYPE, builder.toUriString());
- return new Account(resource, List.of(blog));
- }
- }
- } catch (NullPointerException | IllegalArgumentException e) {
- throw new HttpNotFoundException();
- }
- }
- throw new HttpNotFoundException();
- }
}
diff --git a/src/main/java/com/juick/www/api/webhooks/PatreonWebhook.java b/src/main/java/com/juick/www/api/webhooks/PatreonWebhook.java
index 99999838..c05585ac 100644
--- a/src/main/java/com/juick/www/api/webhooks/PatreonWebhook.java
+++ b/src/main/java/com/juick/www/api/webhooks/PatreonWebhook.java
@@ -32,7 +32,7 @@ import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -67,28 +67,6 @@ public class PatreonWebhook {
var updatedEmails = StreamSupport.stream(json.get("included").spliterator(), false)
.filter(node -> node.get("type").textValue().equals("user"))
.map(node -> node.get("attributes").get("email").textValue()).toList();
- var campainsResponse = patreonService.fetchCampaigns();
- List<String> activeEmails = new ArrayList<>();
- campainsResponse.forEach(campaign -> {
- var pledgesResponse = patreonService.fetchPledges(campaign);
- pledgesResponse.forEach(pledge -> {
- logger.info("Pledge email: {}", pledge);
- activeEmails.add(pledge);
- });
- });
- activeEmails.forEach(email -> {
- var user = userService.getUserByEmail(email);
- if (!user.isAnonymous()) {
- userService.setPremium(user.getUid(), true);
- }
- });
- updatedEmails.stream().filter(email -> !activeEmails.contains(email))
- .forEach(deleted -> {
- var user = userService.getUserByEmail(deleted);
- if (!user.isAnonymous()) {
- logger.info("User is not a patron anymore: {}", deleted);
- userService.setPremium(user.getUid(), false);
- }
- });
+ patreonService.updateStatus(updatedEmails);
}
}
diff --git a/src/main/java/com/juick/www/api/webhooks/TelegramWebhook.java b/src/main/java/com/juick/www/api/webhooks/TelegramWebhook.java
index 76b38168..4636e9f6 100644
--- a/src/main/java/com/juick/www/api/webhooks/TelegramWebhook.java
+++ b/src/main/java/com/juick/www/api/webhooks/TelegramWebhook.java
@@ -32,7 +32,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
diff --git a/src/main/java/com/juick/www/api/webhooks/VkWebhook.java b/src/main/java/com/juick/www/api/webhooks/VkWebhook.java
index 9e4477b1..d69d00be 100644
--- a/src/main/java/com/juick/www/api/webhooks/VkWebhook.java
+++ b/src/main/java/com/juick/www/api/webhooks/VkWebhook.java
@@ -29,7 +29,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
@@ -62,9 +62,9 @@ public class VkWebhook {
if (secretKey.equals(secret)) {
if (type.startsWith("donut_")) {
var vkId = json.get("object").get("user_id").asLong(0);
- var userId = userService.getUIDbyVKID(vkId);
- if (userId > 0) {
- vkService.updatePremiumStatus(userId);
+ var user = userService.getUserByVKID(vkId);
+ if (user.isPresent()) {
+ vkService.updatePremiumStatus(user.get().getUid());
}
}
return "ok";
diff --git a/src/main/java/com/juick/www/api/xnodeinfo2/Info.java b/src/main/java/com/juick/www/api/xnodeinfo2/Info.java
index 2af252ed..adbd5712 100644
--- a/src/main/java/com/juick/www/api/xnodeinfo2/Info.java
+++ b/src/main/java/com/juick/www/api/xnodeinfo2/Info.java
@@ -17,27 +17,14 @@
package com.juick.www.api.xnodeinfo2;
-import com.cliqset.xrd.Link;
-import com.cliqset.xrd.XRD;
import com.fasterxml.jackson.annotation.JsonView;
import com.juick.service.InfoService;
import com.juick.www.api.xnodeinfo2.model.*;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.MediaType;
-import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.util.UriComponentsBuilder;
-
-import javax.inject.Inject;
-import java.net.URI;
-import java.time.ZonedDateTime;
-import java.time.temporal.ChronoUnit;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
+import jakarta.inject.Inject;
@RestController
public class Info {
@@ -53,18 +40,6 @@ public class Info {
return infoService.getCurrentNodeInfo("1.0");
}
- @GetMapping(value = "/.well-known/nodeinfo", produces = MediaType.APPLICATION_JSON_VALUE)
- public XRD getNodeInfoLinks() {
- Link nodeinfo = new Link();
- nodeinfo.setRel(URI.create("http://nodeinfo.diaspora.software/ns/schema/2.0"));
- UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri);
- uriComponentsBuilder.replacePath("/api/nodeinfo/2.0");
- nodeinfo.setHref(uriComponentsBuilder.build().toUri());
- XRD xrd = new XRD();
- xrd.setLinks(Collections.singletonList(nodeinfo));
- return xrd;
- }
-
@GetMapping(value = "/api/nodeinfo/2.0", produces = MediaType.APPLICATION_JSON_VALUE)
@JsonView(NodeInfo.NodeInfoView.class)
public NodeInfo showNodeInfo() {
diff --git a/src/main/java/com/juick/www/controllers/Compat.java b/src/main/java/com/juick/www/controllers/Compat.java
index edd7bc29..334213be 100644
--- a/src/main/java/com/juick/www/controllers/Compat.java
+++ b/src/main/java/com/juick/www/controllers/Compat.java
@@ -30,7 +30,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
@Controller
@Validated
diff --git a/src/main/java/com/juick/www/controllers/Help.java b/src/main/java/com/juick/www/controllers/Help.java
index ae722594..5754e55f 100644
--- a/src/main/java/com/juick/www/controllers/Help.java
+++ b/src/main/java/com/juick/www/controllers/Help.java
@@ -29,7 +29,7 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.util.Locale;
import java.util.Objects;
diff --git a/src/main/java/com/juick/www/controllers/Settings.java b/src/main/java/com/juick/www/controllers/Settings.java
index d95b21b7..11f31efb 100644
--- a/src/main/java/com/juick/www/controllers/Settings.java
+++ b/src/main/java/com/juick/www/controllers/Settings.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2023, Juick
+ * 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
@@ -23,7 +23,7 @@ import java.util.Locale;
import java.util.ResourceBundle;
import java.util.stream.IntStream;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import com.juick.model.User;
import com.juick.service.EmailService;
@@ -150,14 +150,20 @@ public class Settings {
visitor.setCountry(request.getParameter("country"));
visitor.setUrl(request.getParameter("url"));
visitor.setDescription(request.getParameter("descr"));
- String avatarTmpPath = HttpUtils.receiveMultiPartFile(newAvatar, storageService.getTemporaryDirectory()).getHost();
- if (StringUtils.isNotEmpty(avatarTmpPath)) {
- storageService.saveAvatar(avatarTmpPath, visitor);
- }
- if (userService.updateUserInfo(visitor)) {
- result = String.format("<p>Your info is updated.</p><p><a href='/%s/'>Back to blog</a>.</p>", visitor.getName());
+ try {
+ String avatarTmpPath = HttpUtils
+ .receiveMultiPartFile(newAvatar, storageService.getTemporaryDirectory()).getHost();
+ if (StringUtils.isNotEmpty(avatarTmpPath)) {
+ storageService.saveAvatar(avatarTmpPath, visitor);
+ }
+ if (userService.updateUserInfo(visitor)) {
+ result = String.format("<p>Your info is updated.</p><p><a href='/%s/'>Back to blog</a>.</p>",
+ visitor.getName());
+ }
+ applicationEventPublisher.publishEvent(new UpdateUserEvent(this, visitor));
+ } catch (Exception e) {
+ result = "<p>" + e.getMessage() + ". <a href=\"/settings\">Back</a>.</p>";
}
- applicationEventPublisher.publishEvent(new UpdateUserEvent(this, visitor));
break;
case "jid-del":
// FIXME: stop using ugnich-csv in parameters
@@ -175,25 +181,29 @@ public class Settings {
}
break;
case "email-add":
- if (!emailService.verifyAddressByCode(visitor.getUid(), request.getParameter("account"))) {
- String authCode = RandomStringUtils.randomAlphanumeric(8).toUpperCase();
- if (emailService.addVerificationCode(visitor.getUid(), request.getParameter("account"), authCode)) {
- Session session = Session.getDefaultInstance(System.getProperties());
- try {
- MimeMessage message = new MimeMessage(session);
- message.setFrom(new InternetAddress("noreply@juick.com"));
- message.addRecipient(Message.RecipientType.TO, new InternetAddress(request.getParameter("account")));
- message.setSubject("Juick authorization link");
- message.setText(String.format("Follow link to attach this email to Juick account:\n" +
- "http://juick.com/settings?page=auth-email&code=%s\n\n" +
- "If you don't know, what this mean - just ignore this mail.\n", authCode));
- Transport.send(message);
- result = "<p>Authorization link has been sent to your email. Follow it to proceed.</p>" +
- "<p><a href=\"/settings\">Back</a></p>";
+ if (!emailService.isValidEmail(request.getParameter("account"))) {
+ result = "<p>Invalid email. <a href=\"/settings\">Back</a>.</p>";
+ } else {
+ if (!emailService.verifyAddressByCode(visitor.getUid(), request.getParameter("account"))) {
+ String authCode = RandomStringUtils.randomAlphanumeric(8).toUpperCase();
+ if (emailService.addVerificationCode(visitor.getUid(), request.getParameter("account"), authCode)) {
+ Session session = Session.getDefaultInstance(System.getProperties());
+ try {
+ MimeMessage message = new MimeMessage(session);
+ message.setFrom(new InternetAddress("noreply@juick.com"));
+ message.addRecipient(Message.RecipientType.TO, new InternetAddress(request.getParameter("account")));
+ message.setSubject("Juick authorization link");
+ message.setText(String.format("Follow link to attach this email to Juick account:\n" +
+ "https://juick.com/settings?page=auth-email&code=%s\n\n" +
+ "If you don't know, what this mean - just ignore this mail.\n", authCode));
+ Transport.send(message);
+ result = "<p>Authorization link has been sent to your email. Follow it to proceed.</p>" +
+ "<p><a href=\"/settings\">Back</a></p>";
- } catch (MessagingException ex) {
- logger.error("mail exception", ex);
- throw new HttpBadRequestException();
+ } catch (MessagingException ex) {
+ logger.error("mail exception", ex);
+ throw new HttpBadRequestException();
+ }
}
}
}
diff --git a/src/main/java/com/juick/www/controllers/SignUp.java b/src/main/java/com/juick/www/controllers/SignUp.java
index 50ce6955..dddefef3 100644
--- a/src/main/java/com/juick/www/controllers/SignUp.java
+++ b/src/main/java/com/juick/www/controllers/SignUp.java
@@ -37,7 +37,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
/**
*
@@ -92,6 +92,7 @@ public class SignUp {
model.addAttribute("account", account);
model.addAttribute("type", type);
model.addAttribute("hash", hash);
+ model.addAttribute("headers", "<meta name=\"robots\" content=\"noindex\"/>");
return "views/signup";
}
diff --git a/src/main/java/com/juick/www/controllers/Site.java b/src/main/java/com/juick/www/controllers/Site.java
index 3af6f2c2..c5074547 100644
--- a/src/main/java/com/juick/www/controllers/Site.java
+++ b/src/main/java/com/juick/www/controllers/Site.java
@@ -45,7 +45,7 @@ import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.view.RedirectView;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -595,7 +595,7 @@ public class Site {
fillUserModel(model, visitor, visitor);
visitor.setAvatar(webApp.getAvatarWebPath(visitor));
model.addAttribute("title", "Написать");
- model.addAttribute("headers", "");
+ model.addAttribute("headers", "<meta name=\"robots\" content=\"noindex\"/>");
model.addAttribute("visitor", visitor);
if (body == null) {
body = StringUtils.EMPTY;
diff --git a/src/main/java/com/juick/www/controllers/SocialLogin.java b/src/main/java/com/juick/www/controllers/SocialLogin.java
index 75099e50..510c7d62 100644
--- a/src/main/java/com/juick/www/controllers/SocialLogin.java
+++ b/src/main/java/com/juick/www/controllers/SocialLogin.java
@@ -53,11 +53,12 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.IOException;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
@@ -70,7 +71,7 @@ import java.util.stream.Collectors;
@Controller
public class SocialLogin {
- private static final Logger logger = LoggerFactory.getLogger(SocialLogin.class);
+ private static final Logger logger = LoggerFactory.getLogger("Social");
public static final String AUTH_ERROR = "SocialLogin.AuthenticationError";
@@ -124,12 +125,12 @@ public class SocialLogin {
@GetMapping("/_fblogin")
protected String doFacebookLogin(HttpServletRequest request, @RequestParam(required = false) String code,
@RequestParam(required = false) String state,
- @RequestHeader(value = "referer", required = false) String referer, HttpServletResponse response,
+ HttpServletResponse response,
HttpSession session) throws IOException, ExecutionException, InterruptedException {
if (StringUtils.isBlank(code)) {
String fbstate = UUID.randomUUID().toString();
if (StringUtils.isBlank(state)) {
- state = Optional.ofNullable(referer).orElse("https://juick.com/");
+ state = UUID.randomUUID().toString();
}
userService.addFacebookState(fbstate, state);
return "redirect:" + facebookAuthService.getAuthorizationUrl(fbstate);
@@ -170,10 +171,12 @@ public class SocialLogin {
}
}
if (!existingFacebookUser.get().isBanned()) {
- Cookie c = new Cookie("hash", userService.getHashByUID(existingFacebookUser.get().getUid()));
- c.setMaxAge(50 * 24 * 60 * 60);
- response.addCookie(c);
- return "redirect:" + redirectUrl;
+ var authentication = new RememberMeAuthenticationToken(
+ ((AbstractRememberMeServices) rememberMeServices).getKey(),
+ new JuickUser(existingFacebookUser.get()), JuickUser.USER_AUTHORITY);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ rememberMeServices.loginSuccess(request, response, authentication);
+ return "redirect:/";
} else {
session.setAttribute(SocialLogin.AUTH_ERROR, "User is disabled");
return "redirect:/login";
@@ -188,9 +191,9 @@ public class SocialLogin {
@GetMapping("/_twitter")
protected String doTwitterLogin(@RequestParam(required = false) String code,
- @RequestParam(required = false) String state,
- com.juick.model.User user,
- HttpServletRequest request)
+ @RequestParam(required = false) String state,
+ com.juick.model.User user,
+ HttpServletRequest request)
throws IOException, ExecutionException, InterruptedException {
if (StringUtils.isBlank(code)) {
@@ -229,14 +232,17 @@ public class SocialLogin {
@GetMapping("/_vklogin")
protected String doVKLogin(@RequestParam(required = false) String code,
@RequestParam(required = false) String state,
- @RequestHeader(value = "referer", required = false) String referer,
- @CookieValue(required = false) String vkstate, HttpServletResponse response)
+ @CookieValue(required = false) String vkstate,
+ HttpServletRequest request,
+ HttpServletResponse response,
+ HttpSession session)
throws IOException, ExecutionException, InterruptedException {
if (StringUtils.isBlank(code)) {
vkstate = UUID.randomUUID().toString();
Cookie c = new Cookie("vkstate", vkstate);
response.addCookie(c);
- return "redirect:" + vkService.getVkAuthService().getAuthorizationUrl(vkstate);
+ var redirect = "redirect:" + vkService.getVkAuthService().getAuthorizationUrl(vkstate);
+ return redirect;
}
if (StringUtils.isBlank(vkstate) || !vkstate.equals(state)) {
@@ -265,13 +271,19 @@ public class SocialLogin {
}
long vkID = NumberUtils.toLong(jsonUser.id(), 0);
- int uid = userService.getUIDbyVKID(vkID);
- if (uid > 0) {
- userService.updateVkUser(vkID, token.getAccessToken(), vkName, vkLink);
- Cookie c = new Cookie("hash", userService.getHashByUID(uid));
- c.setMaxAge(50 * 24 * 60 * 60);
- response.addCookie(c);
- return "redirect:/" + Optional.ofNullable(referer).orElse(StringUtils.EMPTY);
+ var user = userService.getUserByVKID(vkID);
+ if (user.isPresent()) {
+ if (!user.get().isBanned()) {
+ var authentication = new RememberMeAuthenticationToken(
+ ((AbstractRememberMeServices) rememberMeServices).getKey(),
+ new JuickUser(user.get()), JuickUser.USER_AUTHORITY);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ rememberMeServices.loginSuccess(request, response, authentication);
+ return "redirect:/";
+ } else {
+ session.setAttribute(SocialLogin.AUTH_ERROR, "User is disabled");
+ return "redirect:/login";
+ }
} else {
String loginhash = UUID.randomUUID().toString();
if (!userService.createVKUser(vkID, loginhash, token.getAccessToken(), vkName, vkLink)) {
@@ -290,8 +302,7 @@ public class SocialLogin {
@GetMapping("/_tglogin")
public String doDurovLogin(@RequestParam Map<String, String> params,
@RequestParam String hash,
- @RequestHeader(value = "referer", required = false) String referer,
- HttpServletRequest request, HttpServletResponse response) {
+ HttpServletRequest request, HttpServletResponse response, HttpSession session) {
String dataCheckString = params.entrySet().stream().filter(p -> !p.getKey().equals("hash"))
.sorted(Map.Entry.comparingByKey()).map(p -> p.getKey() + "=" + p.getValue())
.collect(Collectors.joining("\n"));
@@ -301,14 +312,19 @@ public class SocialLogin {
long tgUser = Long.parseLong(params.get("id"));
var user = userService.getUserByTelegramId(tgUser);
if (user.isPresent()) {
- var authentication = new RememberMeAuthenticationToken(
- ((AbstractRememberMeServices) rememberMeServices).getKey(),
- new JuickUser(user.get()), JuickUser.USER_AUTHORITY);
- SecurityContextHolder.getContext().setAuthentication(authentication);
- rememberMeServices.loginSuccess(request, response, authentication);
- return "redirect:" + Optional.ofNullable(referer).orElse(StringUtils.EMPTY);
+ if (!user.get().isBanned()) {
+ var authentication = new RememberMeAuthenticationToken(
+ ((AbstractRememberMeServices) rememberMeServices).getKey(),
+ new JuickUser(user.get()), JuickUser.USER_AUTHORITY);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ rememberMeServices.loginSuccess(request, response, authentication);
+ return "redirect:/";
+ } else {
+ session.setAttribute(SocialLogin.AUTH_ERROR, "User is disabled");
+ return "redirect:/login";
+ }
} else {
- String username = StringUtils.defaultString(params.get("username"), params.get("first_name"));
+ String username = Objects.toString(params.get("username"), params.get("first_name"));
List<Long> chats = telegramService.getAnonymous();
if (!chats.contains(tgUser)) {
logger.info("added chat with {}", username);
@@ -347,9 +363,11 @@ public class SocialLogin {
com.juick.model.User user = userService.getUserByEmail(email.get());
if (!user.isAnonymous()) {
if (!user.isBanned()) {
- Cookie c = new Cookie("hash", userService.getHashByUID(user.getUid()));
- c.setMaxAge(50 * 24 * 60 * 60);
- response.addCookie(c);
+ var authentication = new RememberMeAuthenticationToken(
+ ((AbstractRememberMeServices) rememberMeServices).getKey(),
+ new JuickUser(user), JuickUser.USER_AUTHORITY);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ rememberMeServices.loginSuccess(request, response, authentication);
return "redirect:/";
} else {
session.setAttribute(SocialLogin.AUTH_ERROR, "User is disabled");
diff --git a/src/main/java/com/juick/www/filters/AnythingFilter.java b/src/main/java/com/juick/www/filters/AnythingFilter.java
index 91422807..463374ee 100644
--- a/src/main/java/com/juick/www/filters/AnythingFilter.java
+++ b/src/main/java/com/juick/www/filters/AnythingFilter.java
@@ -33,7 +33,7 @@ import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.UriComponents;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.io.IOException;
@Component
diff --git a/src/main/java/com/juick/www/rss/Feeds.java b/src/main/java/com/juick/www/rss/Feeds.java
index f19f1505..a0608172 100644
--- a/src/main/java/com/juick/www/rss/Feeds.java
+++ b/src/main/java/com/juick/www/rss/Feeds.java
@@ -30,7 +30,7 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import java.util.List;
/**
diff --git a/src/main/java/com/juick/www/rss/MessagesView.java b/src/main/java/com/juick/www/rss/MessagesView.java
index cb4eea2e..b6b5e5a1 100644
--- a/src/main/java/com/juick/www/rss/MessagesView.java
+++ b/src/main/java/com/juick/www/rss/MessagesView.java
@@ -24,7 +24,7 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
-import javax.inject.Inject;
+import jakarta.inject.Inject;
import com.juick.model.Attachment;
import com.juick.model.Message;
diff --git a/src/main/java/com/juick/www/rss/RepliesView.java b/src/main/java/com/juick/www/rss/RepliesView.java
index 80548a79..47fb46b6 100644
--- a/src/main/java/com/juick/www/rss/RepliesView.java
+++ b/src/main/java/com/juick/www/rss/RepliesView.java
@@ -40,7 +40,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.view.feed.AbstractRssFeedView;
-import javax.annotation.Nonnull;
+import jakarta.annotation.Nonnull;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index fe206251..21c9f17a 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -7,6 +7,9 @@ spring.jackson.serialization.write-dates-as-timestamps=false
spring.jackson.serialization.write-empty-json-arrays=true
spring.h2.console.enabled=false
spring.datasource.generate-unique-name=false
+spring.jpa.generate-ddl=false
+spring.jpa.hibernate.ddl-auto=none
+spring.jpa.open-in-view=false
spring.flyway.enabled=false
spring.sql.init.platform=h2
spring.cache.type=simple
diff --git a/src/main/resources/data-h2.sql b/src/main/resources/data-h2.sql
index 818575e9..72615485 100644
--- a/src/main/resources/data-h2.sql
+++ b/src/main/resources/data-h2.sql
@@ -1,6 +1,7 @@
INSERT INTO users(id, nick, passw) VALUES(0, 'Anonymous', 'password');
INSERT INTO users(id, nick, passw) VALUES(2, 'juick', 'password');
INSERT INTO users(id, nick, passw) VALUES(5, 'archive', 'password');
+INSERT INTO telegram(user_id, tg_id) VALUES(5, '1');
INSERT INTO reactions (like_id, description) VALUES (1, 'like');
INSERT INTO reactions (like_id, description) VALUES (2, 'love');
INSERT INTO reactions (like_id, description) VALUES (3, 'lol');
diff --git a/src/main/resources/db/migration/V1.50__email_created_at.sql b/src/main/resources/db/migration/V1.50__email_created_at.sql
new file mode 100644
index 00000000..aa5b3f4b
--- /dev/null
+++ b/src/main/resources/db/migration/V1.50__email_created_at.sql
@@ -0,0 +1 @@
+ALTER TABLE emails ADD COLUMN created_at timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP
diff --git a/src/main/resources/db/migration/V1.51__cast_users_lang.sql b/src/main/resources/db/migration/V1.51__cast_users_lang.sql
new file mode 100644
index 00000000..bb65a157
--- /dev/null
+++ b/src/main/resources/db/migration/V1.51__cast_users_lang.sql
@@ -0,0 +1,2 @@
+DROP CAST IF EXISTS (varchar AS public.users_lang);
+CREATE CAST (varchar AS public.users_lang) WITH INOUT AS IMPLICIT;
diff --git a/src/main/resources/schema-h2.sql b/src/main/resources/schema-h2.sql
index 5fd7fff8..dc705727 100644
--- a/src/main/resources/schema-h2.sql
+++ b/src/main/resources/schema-h2.sql
@@ -233,7 +233,8 @@ ALTER TABLE "PUBLIC"."USERSINFO" ADD CONSTRAINT "PUBLIC"."CONSTRAINT_BB6" PRIMAR
CREATE MEMORY TABLE "PUBLIC"."EMAILS"(
"USER_ID" INTEGER NOT NULL,
"EMAIL" VARCHAR_IGNORECASE(255) NOT NULL,
- "SUBSCR_HOUR" TINYINT DEFAULT NULL
+ "SUBSCR_HOUR" TINYINT DEFAULT NULL,
+ "CREATED_AT" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
ALTER TABLE "PUBLIC"."EMAILS" ADD CONSTRAINT "PUBLIC"."CONSTRAINT_7A" PRIMARY KEY("EMAIL");
CREATE MEMORY TABLE "PUBLIC"."LOGINS"(
diff --git a/src/main/resources/schema-mysql.sql b/src/main/resources/schema-mysql.sql
index 0e035d5e..813b9e3d 100644
--- a/src/main/resources/schema-mysql.sql
+++ b/src/main/resources/schema-mysql.sql
@@ -72,6 +72,7 @@ CREATE TABLE `emails` (
`user_id` int(10) unsigned NOT NULL,
`email` char(128) NOT NULL,
`subscr_hour` tinyint(4) DEFAULT NULL,
+ `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
KEY `email` (`email`) USING HASH
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
diff --git a/src/main/resources/schema-sqlite.sql b/src/main/resources/schema-sqlite.sql
index 74ba28b6..54160c80 100644
--- a/src/main/resources/schema-sqlite.sql
+++ b/src/main/resources/schema-sqlite.sql
@@ -32,6 +32,7 @@ CREATE TABLE emails (
user_id INTEGER NOT NULL,
email character varying(128) NOT NULL,
subscr_hour smallint,
+ created_at DEFAULT (strftime('%s','now') || substr(strftime('%f','now'),4)) NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE facebook (
diff --git a/src/main/resources/schema-sqlserver.sql b/src/main/resources/schema-sqlserver.sql
index 947e5403..9ff21ed2 100644
--- a/src/main/resources/schema-sqlserver.sql
+++ b/src/main/resources/schema-sqlserver.sql
@@ -56,6 +56,7 @@ CREATE TABLE emails (
user_id bigint NOT NULL,
email character varying(128) NOT NULL,
subscr_hour smallint,
+ created_at datetimeoffset DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE facebook (
diff --git a/src/main/resources/templates/layouts/default.html b/src/main/resources/templates/layouts/default.html
index 3b936e07..b7975b42 100644
--- a/src/main/resources/templates/layouts/default.html
+++ b/src/main/resources/templates/layouts/default.html
@@ -2,34 +2,34 @@
<html prefix="og: http://ogp.me/ns#">
<head>
- <meta charset="utf-8" />
- <meta name="ermp-site-verification" value="6714D81C-9EEF-4283-A726-166680B6ABC8" />
+ <meta charset="utf-8">
+ <meta name="ermp-site-verification" value="6714D81C-9EEF-4283-A726-166680B6ABC8">
<script type="text/javascript" src="{{ beans.webApp.scriptsUrl }}"></script>
- <link rel="stylesheet" type="text/css" href="{{ beans.webApp.styleUrl }}" />
+ <link rel="stylesheet" type="text/css" href="{{ beans.webApp.styleUrl }}">
{% block headers %}
{{ headers | default('') | raw }}
{% endblock %}
<title>{{ title | default('Juick') }}</title>
- <meta property="og:type" content="{{ ogtype | default('website') }}" />
- <meta property="fb:app_id" content="130568668304" />
- <meta property="fb:pages" content="270127573154958" />
- <meta name="viewport" content="width=device-width,initial-scale=1" />
- <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" />
- <meta name="theme-color" content="#383838" media="(prefers-color-scheme: dark)" />
- <meta name="apple-mobile-web-app-capable" content="yes" />
- <link rel="apple-touch-icon" sizes="57x57" href="//i.juick.com/apple-icon-57x57.png" />
- <link rel="apple-touch-icon" sizes="60x60" href="//i.juick.com/apple-icon-60x60.png" />
- <link rel="apple-touch-icon" sizes="72x72" href="//i.juick.com/apple-icon-72x72.png" />
- <link rel="apple-touch-icon" sizes="76x76" href="//i.juick.com/apple-icon-76x76.png" />
- <link rel="apple-touch-icon" sizes="114x114" href="//i.juick.com/apple-icon-114x114.png" />
- <link rel="apple-touch-icon" sizes="120x120" href="//i.juick.com/apple-icon-120x120.png" />
- <link rel="apple-touch-icon" sizes="144x144" href="//i.juick.com/apple-icon-144x144.png" />
- <link rel="apple-touch-icon" sizes="152x152" href="//i.juick.com/apple-icon-152x152.png" />
- <link rel="apple-touch-icon" sizes="180x180" href="//i.juick.com/apple-icon-180x180.png" />
- <link rel="icon" type="image/png" sizes="32x32" href="//i.juick.com/favicon-32x32.png" />
- <link rel="icon" type="image/png" sizes="96x96" href="//i.juick.com/favicon-96x96.png" />
- <link rel="icon" type="image/png" sizes="16x16" href="//i.juick.com/favicon-16x16.png" />
- <link rel="manifest" href="//i.juick.com/manifest.json" />
+ <meta property="og:type" content="{{ ogtype | default('website') }}">
+ <meta property="fb:app_id" content="130568668304">
+ <meta property="fb:pages" content="270127573154958">
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
+ <meta name="theme-color" content="#383838" media="(prefers-color-scheme: dark)">
+ <meta name="web-app-capable" content="yes">
+ <link rel="apple-touch-icon" sizes="57x57" href="//i.juick.com/apple-icon-57x57.png">
+ <link rel="apple-touch-icon" sizes="60x60" href="//i.juick.com/apple-icon-60x60.png">
+ <link rel="apple-touch-icon" sizes="72x72" href="//i.juick.com/apple-icon-72x72.png">
+ <link rel="apple-touch-icon" sizes="76x76" href="//i.juick.com/apple-icon-76x76.png">
+ <link rel="apple-touch-icon" sizes="114x114" href="//i.juick.com/apple-icon-114x114.png">
+ <link rel="apple-touch-icon" sizes="120x120" href="//i.juick.com/apple-icon-120x120.png">
+ <link rel="apple-touch-icon" sizes="144x144" href="//i.juick.com/apple-icon-144x144.png">
+ <link rel="apple-touch-icon" sizes="152x152" href="//i.juick.com/apple-icon-152x152.png">
+ <link rel="apple-touch-icon" sizes="180x180" href="//i.juick.com/apple-icon-180x180.png">
+ <link rel="icon" type="image/png" sizes="32x32" href="//i.juick.com/favicon-32x32.png">
+ <link rel="icon" type="image/png" sizes="96x96" href="//i.juick.com/favicon-96x96.png">
+ <link rel="icon" type="image/png" sizes="16x16" href="//i.juick.com/favicon-16x16.png">
+ <link rel="manifest" href="//i.juick.com/juick.webmanifest">
</head>
<body id="body">
@@ -38,12 +38,13 @@
<div id="content_wrapper">
<aside id="column">
<div id="sidebar_wrapper">
- {% block column %}
- {% endblock %}
+ {% block column %}
+ {% endblock %}
</div>
</aside>
{% flush %}
- <section id="content" {% if msg | default('') is not empty %}data-mid="{{ msg.mid }}" {% endif %} class="content--top">
+ <section id="content" {% if msg | default('') is not empty %}data-mid="{{ msg.mid }}" {% endif %}
+ class="content--top">
{% block content %}
{% endblock %}
</section>
diff --git a/src/main/resources/templates/views/thread.html b/src/main/resources/templates/views/thread.html
index 9aad1ce5..ab751294 100644
--- a/src/main/resources/templates/views/thread.html
+++ b/src/main/resources/templates/views/thread.html
@@ -116,7 +116,7 @@
{% if rec.uri.toString() is empty %}
<a href="/{{ rec.name }}/">@{{ rec.name }}</a>{% if loop.index < (loop.length - 1) %}, {% endif %}
{% else %}
- <a href="{{ rec.uri }}">@{{ rec.name }}</a>{% if loop.index < (loop.length - 1) %}, {% endif %}
+ <a href="{{ rec.uri }}">@{{ rec.name }}<span class="dimmed">@{{ rec.uri.host }}</span></a>{% if loop.index < (loop.length - 1) %}, {% endif %}
{% endif %}
{% endfor %}
</div>