@@ -212,6 +212,7 @@ public class ApiConstants { | |||||
public static final String EP_NODE_MANAGER = "/nodeman"; | public static final String EP_NODE_MANAGER = "/nodeman"; | ||||
public static final String EP_UPGRADE = "/upgrade"; | public static final String EP_UPGRADE = "/upgrade"; | ||||
public static final String EP_LOGS = "/logs"; | public static final String EP_LOGS = "/logs"; | ||||
public static final String EP_FOLLOW = "/follow"; | |||||
public static final String DETECT_ENDPOINT = "/detect"; | public static final String DETECT_ENDPOINT = "/detect"; | ||||
public static final String EP_LOCALE = "/locale"; | public static final String EP_LOCALE = "/locale"; | ||||
@@ -33,6 +33,7 @@ import lombok.extern.slf4j.Slf4j; | |||||
import org.cobbzilla.util.collection.ExpirationEvictionPolicy; | import org.cobbzilla.util.collection.ExpirationEvictionPolicy; | ||||
import org.cobbzilla.util.collection.ExpirationMap; | import org.cobbzilla.util.collection.ExpirationMap; | ||||
import org.cobbzilla.util.http.HttpContentEncodingType; | import org.cobbzilla.util.http.HttpContentEncodingType; | ||||
import org.cobbzilla.util.http.HttpUtil; | |||||
import org.cobbzilla.util.network.NetworkUtil; | import org.cobbzilla.util.network.NetworkUtil; | ||||
import org.cobbzilla.wizard.cache.redis.RedisService; | import org.cobbzilla.wizard.cache.redis.RedisService; | ||||
import org.glassfish.grizzly.http.server.Request; | import org.glassfish.grizzly.http.server.Request; | ||||
@@ -54,8 +55,7 @@ import static bubble.service.stream.HttpStreamDebug.getLogFqdn; | |||||
import static bubble.service.stream.StandardRuleEngineService.MATCHERS_CACHE_TIMEOUT; | import static bubble.service.stream.StandardRuleEngineService.MATCHERS_CACHE_TIMEOUT; | ||||
import static com.google.common.net.HttpHeaders.CONTENT_SECURITY_POLICY; | import static com.google.common.net.HttpHeaders.CONTENT_SECURITY_POLICY; | ||||
import static java.util.Collections.emptyMap; | import static java.util.Collections.emptyMap; | ||||
import static java.util.concurrent.TimeUnit.HOURS; | |||||
import static java.util.concurrent.TimeUnit.MINUTES; | |||||
import static java.util.concurrent.TimeUnit.*; | |||||
import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH; | import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH; | ||||
import static org.cobbzilla.util.collection.ArrayUtil.arrayToString; | import static org.cobbzilla.util.collection.ArrayUtil.arrayToString; | ||||
import static org.cobbzilla.util.daemon.ZillaRuntime.*; | import static org.cobbzilla.util.daemon.ZillaRuntime.*; | ||||
@@ -630,6 +630,18 @@ public class FilterHttpResource { | |||||
return ok_empty(); | return ok_empty(); | ||||
} | } | ||||
private final Map<String, String> redirectCache | |||||
= new ExpirationMap<>(1000, DAYS.toMillis(3), ExpirationEvictionPolicy.atime); | |||||
@POST @Path(EP_FOLLOW+"/{requestId}") | |||||
public Response followLink(@Context Request req, | |||||
@Context ContainerRequest ctx, | |||||
@PathParam("requestId") String requestId, | |||||
JsonNode urlNode) { | |||||
final FilterSubContext filterCtx = new FilterSubContext(req, requestId); | |||||
return ok(redirectCache.computeIfAbsent(urlNode.textValue(), HttpUtil::chaseRedirects)); | |||||
} | |||||
@Path(EP_ASSETS+"/{requestId}/{appId}") | @Path(EP_ASSETS+"/{requestId}/{appId}") | ||||
public AppAssetsResource getAppAssetsResource(@Context Request req, | public AppAssetsResource getAppAssetsResource(@Context Request req, | ||||
@Context ContainerRequest ctx, | @Context ContainerRequest ctx, | ||||
@@ -57,6 +57,29 @@ function {{JS_PREFIX}}_create_button(labelKey, labelDefault, onclick, labelForma | |||||
return btn; | return btn; | ||||
} | } | ||||
function {{JS_PREFIX}}_chase_redirects (a) { | |||||
if (a.className && a.className.indexOf('{{JS_PREFIX}}_followed') !== -1) return; | |||||
if (a.className) { | |||||
a.className = a.className + ' {{JS_PREFIX}}_followed'; | |||||
} else { | |||||
a.className = '{{JS_PREFIX}}_followed'; | |||||
} | |||||
a.rel = 'noopener noreferrer nofollow'; | |||||
fetch('/__bubble/api/filter/follow/{{BUBBLE_REQUEST_ID}}', {method: 'POST', body: JSON.stringify(a.href)}) | |||||
.then(response => response.text()) | |||||
.then(data => { | |||||
if (data && (data.startsWith('http://') || data.startsWith('https://'))) { | |||||
a.href = data; | |||||
} else { | |||||
console.warn('chase_redirects: '+a.href+' returned non-URL response: '+data); | |||||
} | |||||
}) | |||||
.catch((error) => { | |||||
console.error('chase_redirects: error following: '+a.href+': '+error); | |||||
}); | |||||
} | |||||
if (typeof {{PAGE_PREFIX}}_icon_status === 'undefined') { | if (typeof {{PAGE_PREFIX}}_icon_status === 'undefined') { | ||||
{{PAGE_PREFIX}}_screenWidth = function () { return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth }; | {{PAGE_PREFIX}}_screenWidth = function () { return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth }; | ||||
@@ -253,9 +253,9 @@ function {{JS_PREFIX}}_apply_blocks(blocked_users) { | |||||
href = {{JS_PREFIX}}_remove_param(href, '__tn__'); | href = {{JS_PREFIX}}_remove_param(href, '__tn__'); | ||||
href = {{JS_PREFIX}}_remove_param(href, 'refid'); | href = {{JS_PREFIX}}_remove_param(href, 'refid'); | ||||
a.href = href; | a.href = href; | ||||
a.rel = 'noopener noreferrer nofollow'; | |||||
a.removeAttribute('data-gt'); | a.removeAttribute('data-gt'); | ||||
a.removeAttribute('data-sigil'); | a.removeAttribute('data-sigil'); | ||||
{{JS_PREFIX}}_chase_redirects(a); | |||||
}); | }); | ||||
} | } | ||||
} | } |
@@ -68,6 +68,11 @@ function {{JS_PREFIX}}_apply_blocks(blocked_users) { | |||||
continue; | continue; | ||||
} else { | } else { | ||||
// console.log('FOUND tweet node for author: '+authorName); | // console.log('FOUND tweet node for author: '+authorName); | ||||
Array.from(tweet.getElementsByTagName('a')).forEach(a => { | |||||
if (a.href && a.href.indexOf('https://t.co/') !== -1) { | |||||
{{JS_PREFIX}}_chase_redirects(a); | |||||
} | |||||
}); | |||||
} | } | ||||
// have we visited this tweet before? | // have we visited this tweet before? | ||||
@@ -1 +1 @@ | |||||
Subproject commit dcaedf8546b02e475e862657af3fab6cbbbb7754 | |||||
Subproject commit 2b96b8403583585c6d7013f7100d4d29064c408a |