mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 03:39:26 +00:00
Add keepalive
This commit is contained in:
@@ -4,8 +4,12 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
|
||||
@@ -29,4 +33,21 @@ public class OAuth2AuthorizedClientPersistenceConfig {
|
||||
OAuth2AuthorizedClientRepository authorizedClientRepository(OAuth2AuthorizedClientService authorizedClientService) {
|
||||
return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
OAuth2AuthorizedClientManager authorizedClientManager(
|
||||
ClientRegistrationRepository clientRegistrationRepository,
|
||||
OAuth2AuthorizedClientRepository authorizedClientRepository
|
||||
) {
|
||||
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
|
||||
.authorizationCode()
|
||||
.refreshToken()
|
||||
.build();
|
||||
DefaultOAuth2AuthorizedClientManager manager = new DefaultOAuth2AuthorizedClientManager(
|
||||
clientRegistrationRepository,
|
||||
authorizedClientRepository
|
||||
);
|
||||
manager.setAuthorizedClientProvider(authorizedClientProvider);
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package dev.kruhlmann.imgfloat.controller;
|
||||
|
||||
import dev.kruhlmann.imgfloat.util.LogSanitizer;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/session")
|
||||
@SecurityRequirement(name = "twitchOAuth")
|
||||
public class SessionApiController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SessionApiController.class);
|
||||
private final OAuth2AuthorizedClientManager authorizedClientManager;
|
||||
|
||||
public SessionApiController(OAuth2AuthorizedClientManager authorizedClientManager) {
|
||||
this.authorizedClientManager = authorizedClientManager;
|
||||
}
|
||||
|
||||
@GetMapping("/refresh")
|
||||
public ResponseEntity<Void> refreshSession(
|
||||
OAuth2AuthenticationToken oauthToken,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response
|
||||
) {
|
||||
if (oauthToken == null) {
|
||||
return ResponseEntity.ok().cacheControl(CacheControl.noStore()).build();
|
||||
}
|
||||
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
|
||||
.withClientRegistrationId(oauthToken.getAuthorizedClientRegistrationId())
|
||||
.principal(oauthToken)
|
||||
.attribute(HttpServletRequest.class.getName(), request)
|
||||
.attribute(HttpServletResponse.class.getName(), response)
|
||||
.build();
|
||||
OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);
|
||||
if (authorizedClient == null) {
|
||||
LOG.warn(
|
||||
"Failed to refresh session for {}",
|
||||
LogSanitizer.sanitize(oauthToken.getName())
|
||||
);
|
||||
}
|
||||
return ResponseEntity.ok().cacheControl(CacheControl.noStore()).build();
|
||||
}
|
||||
}
|
||||
38
src/main/resources/static/js/session-refresh.js
Normal file
38
src/main/resources/static/js/session-refresh.js
Normal file
@@ -0,0 +1,38 @@
|
||||
(() => {
|
||||
const refreshDelayMs = 4 * 60 * 1000;
|
||||
let refreshInterval;
|
||||
|
||||
const refresh = () => {
|
||||
fetch("/api/session/refresh", { cache: "no-store" }).catch(() => null);
|
||||
};
|
||||
|
||||
const start = () => {
|
||||
if (refreshInterval) {
|
||||
return;
|
||||
}
|
||||
refresh();
|
||||
refreshInterval = globalThis.setInterval(refresh, refreshDelayMs);
|
||||
};
|
||||
|
||||
const stop = () => {
|
||||
if (refreshInterval) {
|
||||
clearInterval(refreshInterval);
|
||||
refreshInterval = null;
|
||||
}
|
||||
};
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", start, { once: true });
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
|
||||
globalThis.addEventListener("beforeunload", stop);
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.hidden) {
|
||||
stop();
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -525,6 +525,7 @@
|
||||
const SETTINGS = JSON.parse(/*[[${settingsJson}]]*/);
|
||||
const ADMIN_CHANNELS = /*[[${adminChannels}]]*/ [];
|
||||
</script>
|
||||
<script src="/js/session-refresh.js"></script>
|
||||
<script src="/js/cookie-consent.js"></script>
|
||||
<script src="/js/toast.js"></script>
|
||||
<script type="module" src="/js/admin.js"></script>
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
<script th:inline="javascript">
|
||||
const broadcaster = /*[[${broadcaster}]]*/ "";
|
||||
</script>
|
||||
<script src="/js/session-refresh.js"></script>
|
||||
<script type="module" src="/js/audit-log.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -178,6 +178,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/cookie-consent.js"></script>
|
||||
<script src="/js/session-refresh.js"></script>
|
||||
<script src="/js/csrf.js"></script>
|
||||
<script src="/js/toast.js"></script>
|
||||
<script src="/js/downloads.js"></script>
|
||||
|
||||
@@ -253,6 +253,7 @@
|
||||
const serverRenderedInitialSysadmin = /*[[${initialSysadmin}]]*/;
|
||||
</script>
|
||||
<script src="/js/cookie-consent.js"></script>
|
||||
<script src="/js/session-refresh.js"></script>
|
||||
<script src="/js/settings.js"></script>
|
||||
<script src="/js/toast.js"></script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user