add sitemap, robots.txt and favicon stuff

This commit is contained in:
MiniDigger 2020-08-22 17:33:39 +02:00
parent 2e46930522
commit 66eb9fcf92
7 changed files with 212 additions and 66 deletions

View File

@ -34,6 +34,7 @@
<freemarker-java8.version>2.0.0</freemarker-java8.version>
<flexmark-all.version>0.62.2</flexmark-all.version>
<snakeyaml.version>1.26</snakeyaml.version>
<jsitemapgenerator.version>4.5</jsitemapgenerator.version>
<!-- webjars dependencies -->
<webjars-locator.version>0.40</webjars-locator.version>
@ -171,6 +172,12 @@
<version>${flexmark-all.version}</version>
</dependency>
<dependency>
<groupId>cz.jiripinkas</groupId>
<artifactId>jsitemapgenerator</artifactId>
<version>${jsitemapgenerator.version}</version>
</dependency>
<!-- webjars -->
<dependency>
<groupId>org.webjars</groupId>

View File

@ -1,27 +1,9 @@
package io.papermc.hangar.controller;
import io.papermc.hangar.model.Visibility;
import io.papermc.hangar.model.viewhelpers.Activity;
import io.papermc.hangar.model.viewhelpers.ProjectFlag;
import io.papermc.hangar.service.UserService;
import io.papermc.hangar.service.project.FlagService;
import io.papermc.hangar.util.AlertUtil;
import io.papermc.hangar.db.customtypes.LoggedActionType;
import io.papermc.hangar.db.customtypes.LoggedActionType.ProjectContext;
import io.papermc.hangar.model.NamedPermission;
import io.papermc.hangar.model.Permission;
import io.papermc.hangar.model.viewhelpers.LoggedActionViewModel;
import io.papermc.hangar.model.viewhelpers.ReviewQueueEntry;
import io.papermc.hangar.model.viewhelpers.UnhealthyProject;
import io.papermc.hangar.model.viewhelpers.UserData;
import io.papermc.hangar.security.annotations.GlobalPermission;
import io.papermc.hangar.service.JobService;
import io.papermc.hangar.service.UserActionLogService;
import io.papermc.hangar.service.VersionService;
import io.papermc.hangar.service.project.ProjectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
@ -34,10 +16,31 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import io.papermc.hangar.db.customtypes.LoggedActionType;
import io.papermc.hangar.db.customtypes.LoggedActionType.ProjectContext;
import io.papermc.hangar.model.NamedPermission;
import io.papermc.hangar.model.Permission;
import io.papermc.hangar.model.Visibility;
import io.papermc.hangar.model.viewhelpers.Activity;
import io.papermc.hangar.model.viewhelpers.LoggedActionViewModel;
import io.papermc.hangar.model.viewhelpers.ProjectFlag;
import io.papermc.hangar.model.viewhelpers.ReviewQueueEntry;
import io.papermc.hangar.model.viewhelpers.UnhealthyProject;
import io.papermc.hangar.model.viewhelpers.UserData;
import io.papermc.hangar.security.annotations.GlobalPermission;
import io.papermc.hangar.service.JobService;
import io.papermc.hangar.service.SitemapService;
import io.papermc.hangar.service.UserActionLogService;
import io.papermc.hangar.service.UserService;
import io.papermc.hangar.service.VersionService;
import io.papermc.hangar.service.project.FlagService;
import io.papermc.hangar.service.project.ProjectService;
import io.papermc.hangar.util.AlertUtil;
@Controller
public class ApplicationController extends HangarController {
@ -48,17 +51,19 @@ public class ApplicationController extends HangarController {
private final UserActionLogService userActionLogService;
private final VersionService versionService;
private final JobService jobService;
private final SitemapService sitemapService;
private final HttpServletRequest request;
@Autowired
public ApplicationController(UserService userService, ProjectService projectService, VersionService versionService, FlagService flagService, UserActionLogService userActionLogService, JobService jobService, HttpServletRequest request) {
public ApplicationController(UserService userService, ProjectService projectService, VersionService versionService, FlagService flagService, UserActionLogService userActionLogService, JobService jobService, SitemapService sitemapService, HttpServletRequest request) {
this.userService = userService;
this.projectService = projectService;
this.flagService = flagService;
this.userActionLogService = userActionLogService;
this.versionService = versionService;
this.jobService = jobService;
this.sitemapService = sitemapService;
this.request = request;
}
@ -201,15 +206,16 @@ public class ApplicationController extends HangarController {
return fillModel(new ModelAndView("swagger"));
}
@RequestMapping("/favicon.ico")
@RequestMapping(value = "/favicon.ico", produces = "images/x-icon")
@ResponseBody
public Object faviconRedirect() {
return "no u"; // TODO implement faviconRedirect request controller
public ClassPathResource faviconRedirect() {
return new ClassPathResource("public/images/favicon.ico");
}
@RequestMapping("/global-sitemap.xml")
public Object globalSitemap() {
return null; // TODO implement globalSitemap request controller
@RequestMapping(value = "/global-sitemap.xml", produces = MediaType.APPLICATION_XML_VALUE)
@ResponseBody
public String globalSitemap() {
return sitemapService.getGlobalSitemap();
}
@GetMapping(value = "/javascriptRoutes", produces = "text/javascript")
@ -217,31 +223,31 @@ public class ApplicationController extends HangarController {
public String javaScriptRoutes() {
// yeah, dont even ask wtf is happening here, I dont have an answer
return "var jsRoutes = {}; (function(_root){\n" +
"var _nS = function(c,f,b){var e=c.split(f||\".\"),g=b||_root,d,a;for(d=0,a=e.length;d<a;d++){g=g[e[d]]=g[e[d]]||{}}return g}\n" +
"var _qS = function(items){var qs = ''; for(var i=0;i<items.length;i++) {if(items[i]) qs += (qs ? '&' : '') + items[i]}; return qs ? ('?' + qs) : ''}\n" +
"var _s = function(p,s){return p+((s===true||(s&&s.secure))?'s':'')+'://'}\n" +
"var _wA = function(r){return {ajax:function(c){c=c||{};c.url=r.url;c.type=r.method;return jQuery.ajax(c)}, method:r.method,type:r.method,url:r.url,absoluteURL: function(s){return _s('http',s)+'localhost:8080'+r.url},webSocketURL: function(s){return _s('ws',s)+'localhost:9000'+r.url}}}\n" +
"_nS('controllers.project.Projects'); _root['controllers']['project']['Projects']['show'] = \n" +
" function(author0,slug1) {\n" +
" return _wA({method:\"GET\", url:\"/\" + encodeURIComponent((function(k,v) {return v})(\"author\", author0)) + \"/\" + encodeURIComponent((function(k,v) {return v})(\"slug\", slug1))})\n" +
" }\n" +
" ;\n" +
"_nS('controllers.project.Versions'); _root['controllers']['project']['Versions']['show'] = \n" +
" function(author0,slug1,version2) {\n" +
" return _wA({method:\"GET\", url:\"/\" + encodeURIComponent((function(k,v) {return v})(\"author\", author0)) + \"/\" + encodeURIComponent((function(k,v) {return v})(\"slug\", slug1)) + \"/versions/\" + encodeURIComponent((function(k,v) {return v})(\"version\", version2))})\n" +
" }\n" +
" ;\n" +
"_nS('controllers.project.Versions'); _root['controllers']['project']['Versions']['showCreator'] = \n" +
" function(author0,slug1) {\n" +
" return _wA({method:\"GET\", url:\"/\" + encodeURIComponent((function(k,v) {return v})(\"author\", author0)) + \"/\" + encodeURIComponent((function(k,v) {return v})(\"slug\", slug1)) + \"/versions/new\"})\n" +
" }\n" +
" ;\n" +
"_nS('controllers.Users'); _root['controllers']['Users']['showProjects'] = \n" +
" function(user0) {\n" +
" return _wA({method:\"GET\", url:\"/\" + encodeURIComponent((function(k,v) {return v})(\"user\", user0))})\n" +
" }\n" +
" ;\n" +
"})(jsRoutes)"; // TODO implement javaScriptRoutes request controller
"var _nS = function(c,f,b){var e=c.split(f||\".\"),g=b||_root,d,a;for(d=0,a=e.length;d<a;d++){g=g[e[d]]=g[e[d]]||{}}return g}\n" +
"var _qS = function(items){var qs = ''; for(var i=0;i<items.length;i++) {if(items[i]) qs += (qs ? '&' : '') + items[i]}; return qs ? ('?' + qs) : ''}\n" +
"var _s = function(p,s){return p+((s===true||(s&&s.secure))?'s':'')+'://'}\n" +
"var _wA = function(r){return {ajax:function(c){c=c||{};c.url=r.url;c.type=r.method;return jQuery.ajax(c)}, method:r.method,type:r.method,url:r.url,absoluteURL: function(s){return _s('http',s)+'localhost:8080'+r.url},webSocketURL: function(s){return _s('ws',s)+'localhost:9000'+r.url}}}\n" +
"_nS('controllers.project.Projects'); _root['controllers']['project']['Projects']['show'] = \n" +
" function(author0,slug1) {\n" +
" return _wA({method:\"GET\", url:\"/\" + encodeURIComponent((function(k,v) {return v})(\"author\", author0)) + \"/\" + encodeURIComponent((function(k,v) {return v})(\"slug\", slug1))})\n" +
" }\n" +
" ;\n" +
"_nS('controllers.project.Versions'); _root['controllers']['project']['Versions']['show'] = \n" +
" function(author0,slug1,version2) {\n" +
" return _wA({method:\"GET\", url:\"/\" + encodeURIComponent((function(k,v) {return v})(\"author\", author0)) + \"/\" + encodeURIComponent((function(k,v) {return v})(\"slug\", slug1)) + \"/versions/\" + encodeURIComponent((function(k,v) {return v})(\"version\", version2))})\n" +
" }\n" +
" ;\n" +
"_nS('controllers.project.Versions'); _root['controllers']['project']['Versions']['showCreator'] = \n" +
" function(author0,slug1) {\n" +
" return _wA({method:\"GET\", url:\"/\" + encodeURIComponent((function(k,v) {return v})(\"author\", author0)) + \"/\" + encodeURIComponent((function(k,v) {return v})(\"slug\", slug1)) + \"/versions/new\"})\n" +
" }\n" +
" ;\n" +
"_nS('controllers.Users'); _root['controllers']['Users']['showProjects'] = \n" +
" function(user0) {\n" +
" return _wA({method:\"GET\", url:\"/\" + encodeURIComponent((function(k,v) {return v})(\"user\", user0))})\n" +
" }\n" +
" ;\n" +
"})(jsRoutes)"; // TODO implement javaScriptRoutes request controller
}
@RequestMapping("/linkout")
@ -263,18 +269,16 @@ public class ApplicationController extends HangarController {
return null; // TODO implement actorTree request controller
}
@RequestMapping("/robots.txt")
@RequestMapping(value = "/robots.txt", produces = MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
public Object robots() {
return null; // TODO implement robots request controller
return new ClassPathResource("public/robots.txt");
}
@RequestMapping("/sitemap.xml")
public Object sitemapIndex() {
return null; // TODO implement sitemapIndex request controller
@RequestMapping(value = "/sitemap.xml", produces = MediaType.APPLICATION_XML_VALUE)
@ResponseBody
public String sitemapIndex() {
return sitemapService.getSitemap();
}
// @RequestMapping("/{path}/")
// public Object removeTrail(@PathVariable Object path) {
// return null; // implement removeTrail request controller - pretty sure this one is dum
// }
}

View File

@ -8,6 +8,7 @@ import io.papermc.hangar.db.model.UsersTable;
import io.papermc.hangar.model.InviteFilter;
import io.papermc.hangar.model.NotificationFilter;
import io.papermc.hangar.service.NotificationService;
import io.papermc.hangar.service.SitemapService;
import io.papermc.hangar.service.SsoService;
import io.papermc.hangar.service.UserService;
import io.papermc.hangar.service.sso.AuthUser;
@ -26,6 +27,7 @@ import io.papermc.hangar.util.HangarException;
import io.papermc.hangar.util.RouteHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
@ -35,6 +37,7 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;
@ -60,13 +63,14 @@ public class UsersController extends HangarController {
private final SsoService ssoService;
private final UserActionLogService userActionLogService;
private final HangarDao<UserDao> userDao;
private final SitemapService sitemapService;
private final HttpServletRequest request;
private final HttpServletResponse response;
@Autowired
public UsersController(HangarConfig hangarConfig, RouteHelper routeHelper, AuthenticationService authenticationService, UserService userService, RoleService roleService, ApiKeyService apiKeyService, PermissionService permissionService, NotificationService notificationService, SsoService ssoService, UserActionLogService userActionLogService, HangarDao<UserDao> userDao, HttpServletRequest request, HttpServletResponse response) {
public UsersController(HangarConfig hangarConfig, RouteHelper routeHelper, AuthenticationService authenticationService, UserService userService, RoleService roleService, ApiKeyService apiKeyService, PermissionService permissionService, NotificationService notificationService, SsoService ssoService, UserActionLogService userActionLogService, HangarDao<UserDao> userDao, SitemapService sitemapService, HttpServletRequest request, HttpServletResponse response) {
this.hangarConfig = hangarConfig;
this.routeHelper = routeHelper;
this.authenticationService = authenticationService;
@ -78,6 +82,7 @@ public class UsersController extends HangarController {
this.ssoService = ssoService;
this.userActionLogService = userActionLogService;
this.userDao = userDao;
this.sitemapService = sitemapService;
this.request = request;
this.response = response;
}
@ -246,9 +251,15 @@ public class UsersController extends HangarController {
return new ModelAndView("redirect:" + routeHelper.getRouteUrl("users.showProjects", user));
}
@RequestMapping("/{user}/sitemap.xml")
public Object userSitemap(@PathVariable Object user) {
return null; // TODO implement userSitemap request controller
@RequestMapping(value = "/{user}/sitemap.xml", produces = MediaType.APPLICATION_XML_VALUE)
@ResponseBody
public String userSitemap(@PathVariable String user) {
UsersTable usersTable = userDao.get().getByName(user);
if (usersTable == null) {
response.setStatus(404);
return null;
}
return sitemapService.getUserSitemap(usersTable);
}
}

View File

@ -127,4 +127,10 @@ public interface UserDao {
" LIMIT 20")
@RegisterBeanMapper(FlagActivity.class)
List<FlagActivity> getFlagActivity(String username);
@SqlQuery("SELECT u.name" +
" FROM users u" +
" ORDER BY (SELECT COUNT(*) FROM project_members_all pma WHERE pma.user_id = u.id) DESC" +
" LIMIT 49000")
List<String> getAllAuthorNames();
}

View File

@ -3,6 +3,7 @@ package io.papermc.hangar.model.viewhelpers;
import io.papermc.hangar.model.Visibility;
import io.papermc.hangar.model.generated.ProjectNamespace;
import org.jdbi.v3.core.enums.EnumByOrdinal;
import org.jdbi.v3.core.mapper.Nested;
import java.time.OffsetDateTime;
@ -50,10 +51,12 @@ public class UnhealthyProject {
this.lastUpdated = lastUpdated;
}
@EnumByOrdinal
public Visibility getVisibility() {
return visibility;
}
@EnumByOrdinal
public void setVisibility(Visibility visibility) {
this.visibility = visibility;
}

View File

@ -0,0 +1,85 @@
package io.papermc.hangar.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.List;
import cz.jiripinkas.jsitemapgenerator.ChangeFreq;
import cz.jiripinkas.jsitemapgenerator.WebPage;
import cz.jiripinkas.jsitemapgenerator.generator.SitemapGenerator;
import io.papermc.hangar.config.hangar.HangarConfig;
import io.papermc.hangar.db.dao.HangarDao;
import io.papermc.hangar.db.dao.ProjectDao;
import io.papermc.hangar.db.dao.ProjectPageDao;
import io.papermc.hangar.db.dao.ProjectVersionDao;
import io.papermc.hangar.db.dao.UserDao;
import io.papermc.hangar.db.model.ProjectVersionsTable;
import io.papermc.hangar.db.model.ProjectsTable;
import io.papermc.hangar.db.model.UsersTable;
import io.papermc.hangar.model.viewhelpers.ProjectPage;
@Service
public class SitemapService {
private final HangarConfig hangarConfig;
private final HangarDao<UserDao> userDao;
private final HangarDao<ProjectDao> projectDao;
private final HangarDao<ProjectVersionDao> versionDao;
private final HangarDao<ProjectPageDao> pageDao;
@Autowired
public SitemapService(HangarConfig hangarConfig, HangarDao<UserDao> userDao, HangarDao<ProjectDao> projectDao, HangarDao<ProjectVersionDao> versionDao, HangarDao<ProjectPageDao> pageDao) {
this.hangarConfig = hangarConfig;
this.userDao = userDao;
this.projectDao = projectDao;
this.versionDao = versionDao;
this.pageDao = pageDao;
}
@Cacheable("indexSitemap")
public String getSitemap() {
SitemapGenerator generator = SitemapGenerator.of(hangarConfig.getBaseUrl())
.addPage(WebPage.builder().name("global-sitemap.xml").build());
userDao.get().getAllAuthorNames().forEach(user -> generator.addPage(user + "/sitemap.xml"));
return generator.toString();
}
@Cacheable("globalSitemap")
public String getGlobalSitemap() {
return SitemapGenerator.of(hangarConfig.getBaseUrl())
.addPage(WebPage.builder().name("").changeFreq(ChangeFreq.HOURLY).build())
.addPage(WebPage.builder().name("authors").changeFreq(ChangeFreq.WEEKLY).build())
.addPage(WebPage.builder().name("api").build())
.toString();
}
@Cacheable(value = "userSitemap", key = "#user.name")
public String getUserSitemap(UsersTable user) {
SitemapGenerator generator = SitemapGenerator.of(hangarConfig.getBaseUrl());
// add all projects
List<ProjectsTable> projects = projectDao.get().getProjectsByUserId(user.getId());
projects.forEach(p -> generator.addPage(user.getName() + "/" + p.getSlug()));
// add all versions of said projects
projects.forEach(p -> {
List<ProjectVersionsTable> versions = versionDao.get().getProjectVersions(p.getId());
versions.forEach(v -> generator.addPage(user.getName() + "/" + p.getSlug() + "/" + v.getVersionString()));
});
// add all pages of said projects
projects.forEach(project -> {
List<ProjectPage> pages = pageDao.get().getPages(project.getPluginId());
pages.forEach(page -> generator.addPage(user.getName() + "/" + project.getSlug() + "/" + page.getSlug()));
});
// lastly, add user page
generator.addPage(WebPage.builder().name(user.getName()).changeFreq(ChangeFreq.WEEKLY).build());
return generator.toString();
}
}

View File

@ -0,0 +1,30 @@
user-agent: *
Disallow: /login
Disallow: /signup
Disallow: /logout
Disallow: /linkout
Disallow: /admin
Disallow: /api
Disallow: /*/*/versions
Disallow: /*/*/watchers
Disallow: /*/*/stars
Disallow: /*/*/discuss
Disallow: /*/*/channels
Disallow: /*/*/manage
Disallow: /*/*/versionLog
Disallow: /*/*/flags
Disallow: /*/*/notes
Disallow: /*/*/channels
Allow: /*/*/versions/*
Allow: /api$
Disallow: /*/*/versions/*/download
Disallow: /*/*/versions/*/recommended/download
Disallow: /*/*/versions/*/jar
Disallow: /*/*/versions/*/recommended/jar
Disallow: /*/*/versions/*/reviews
Disallow: /*/*/versions/*/new
Disallow: /*/*/versions/*/confirm
Sitemap: https://hangar-new.minidigger.me/sitemap.xml