diff --git a/backend/pom.xml b/backend/pom.xml
index 8d2e95409..b8b6cdc03 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -37,9 +37,10 @@
4.5
20220924
7.6.0
-
+ 1.6.0
3.12.0
4.2.1
+
1.17.5
@@ -226,6 +227,13 @@
${spring-cloud-aws.version}
+
+
+ net.datafaker
+ datafaker
+ ${datafaker.version}
+
+
org.springframework.boot
diff --git a/backend/src/main/java/io/papermc/hangar/controller/internal/FakeDataController.java b/backend/src/main/java/io/papermc/hangar/controller/internal/FakeDataController.java
new file mode 100644
index 000000000..e43eeb0c4
--- /dev/null
+++ b/backend/src/main/java/io/papermc/hangar/controller/internal/FakeDataController.java
@@ -0,0 +1,31 @@
+package io.papermc.hangar.controller.internal;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import io.papermc.hangar.model.common.NamedPermission;
+import io.papermc.hangar.security.annotations.permission.PermissionRequired;
+import io.papermc.hangar.security.annotations.ratelimit.RateLimit;
+import io.papermc.hangar.service.internal.FakeDataService;
+
+@Controller
+@RateLimit(path = "admin")
+@RequestMapping("/api/internal/fakeData")
+public class FakeDataController {
+
+ private final FakeDataService fakeDataService;
+
+ public FakeDataController(FakeDataService fakeDataService) {
+ this.fakeDataService = fakeDataService;
+ }
+
+ @ResponseStatus(HttpStatus.OK)
+ @GetMapping("/")
+ @PermissionRequired(NamedPermission.MANUAL_VALUE_CHANGES)
+ public void generateFakeData(@RequestParam int users, @RequestParam int projectsPerUser) {
+ fakeDataService.generate(users, projectsPerUser);
+ }
+}
diff --git a/backend/src/main/java/io/papermc/hangar/model/Model.java b/backend/src/main/java/io/papermc/hangar/model/Model.java
index 8d26e3f00..f294f1e0c 100644
--- a/backend/src/main/java/io/papermc/hangar/model/Model.java
+++ b/backend/src/main/java/io/papermc/hangar/model/Model.java
@@ -15,6 +15,10 @@ public abstract class Model {
return this.createdAt;
}
+ public void setCreatedAt(OffsetDateTime createdAt) {
+ this.createdAt = createdAt;
+ }
+
@Override
public boolean equals(final Object o) {
if (this == o) return true;
diff --git a/backend/src/main/java/io/papermc/hangar/model/common/roles/GlobalRole.java b/backend/src/main/java/io/papermc/hangar/model/common/roles/GlobalRole.java
index b5e205c73..79773192f 100644
--- a/backend/src/main/java/io/papermc/hangar/model/common/roles/GlobalRole.java
+++ b/backend/src/main/java/io/papermc/hangar/model/common/roles/GlobalRole.java
@@ -20,6 +20,8 @@ public enum GlobalRole implements Role {
PAPERMC_CORE("PaperMC_Core", 4, Permission.All, "PaperMC Core", Color.AMBER, 10),
PAPERMC_STAFF("PaperMC_Staff", 5, Permission.IsStaff, "Paper Staff", Color.AMBER, 50),
+ DUMMY("Dummy", 42, Permission.ViewPublicInfo, "Dummy", Color.CHARTREUSE, 42),
+
ORGANIZATION("Organization", 100, OrganizationRole.ORGANIZATION_OWNER.getPermissions(), "Organization", Color.PURPLE);
private final String value;
diff --git a/backend/src/main/java/io/papermc/hangar/service/internal/FakeDataService.java b/backend/src/main/java/io/papermc/hangar/service/internal/FakeDataService.java
new file mode 100644
index 000000000..1e41340e1
--- /dev/null
+++ b/backend/src/main/java/io/papermc/hangar/service/internal/FakeDataService.java
@@ -0,0 +1,116 @@
+package io.papermc.hangar.service.internal;
+
+import net.datafaker.Faker;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+import java.time.ZoneOffset;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import io.papermc.hangar.HangarComponent;
+import io.papermc.hangar.db.dao.internal.table.UserDAO;
+import io.papermc.hangar.db.dao.internal.table.projects.ProjectsDAO;
+import io.papermc.hangar.model.api.project.ProjectDonationSettings;
+import io.papermc.hangar.model.api.project.ProjectLicense;
+import io.papermc.hangar.model.api.project.ProjectSettings;
+import io.papermc.hangar.model.common.Permission;
+import io.papermc.hangar.model.common.projects.Category;
+import io.papermc.hangar.model.common.projects.Visibility;
+import io.papermc.hangar.model.common.roles.GlobalRole;
+import io.papermc.hangar.model.db.UserTable;
+import io.papermc.hangar.model.db.projects.ProjectTable;
+import io.papermc.hangar.model.db.roles.GlobalRoleTable;
+import io.papermc.hangar.model.internal.api.requests.projects.NewProjectForm;
+import io.papermc.hangar.security.authentication.HangarAuthenticationToken;
+import io.papermc.hangar.security.authentication.HangarPrincipal;
+import io.papermc.hangar.service.internal.perms.roles.GlobalRoleService;
+import io.papermc.hangar.service.internal.projects.ProjectFactory;
+import io.papermc.hangar.service.internal.projects.ProjectService;
+
+@Service
+public class FakeDataService extends HangarComponent {
+
+ private final Faker faker = new Faker();
+
+ private final UserDAO userDAO;
+ private final GlobalRoleService globalRoleService;
+ private final ProjectService projectService;
+ private final ProjectFactory projectFactory;
+ private final ProjectsDAO projectsDAO;
+
+ public FakeDataService(UserDAO userDAO, GlobalRoleService globalRoleService, ProjectService projectService, ProjectFactory projectFactory, ProjectsDAO projectsDAO) {
+ this.userDAO = userDAO;
+ this.globalRoleService = globalRoleService;
+ this.projectService = projectService;
+ this.projectFactory = projectFactory;
+ this.projectsDAO = projectsDAO;
+ }
+
+ public void generate(int users, int projectsPerUser) {
+ HangarAuthenticationToken oldAuth = (HangarAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
+ try {
+ for (int udx = 0; udx < users; udx++) {
+ UserTable user = createUser();
+ SecurityContextHolder.getContext().setAuthentication(HangarAuthenticationToken.createVerifiedToken(new HangarPrincipal(user.getUserId(), user.getName(), false, Permission.All), oldAuth.getCredentials()));
+ for (int pdx = 0; pdx < projectsPerUser; pdx++) {
+ createProject(user.getUserId());
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ SecurityContextHolder.getContext().setAuthentication(oldAuth);
+ projectService.refreshHomeProjects();
+ }
+ }
+
+ public UserTable createUser() {
+ UserTable userTable = userDAO.create(UUID.randomUUID(),
+ normalize(faker.simpsons().character()) + faker.random().nextInt(500),
+ faker.internet().safeEmailAddress(),
+ faker.chuckNorris().fact(),
+ "en",
+ List.of(),
+ false,
+ "dark");
+ globalRoleService.addRole(new GlobalRoleTable(userTable.getId(), GlobalRole.DUMMY));
+ return userTable;
+ }
+
+ public void createProject(long ownerId) {
+ ProjectLicense licence = new ProjectLicense(config.getLicenses().get(faker.random().nextInt(config.getLicenses().size())), null);
+ Set keyWords = new HashSet<>();
+ for (int i = 0; i < faker.random().nextInt(2, 5); i++) {
+ keyWords.add(faker.marketing().buzzwords());
+ }
+ ProjectSettings settings = new ProjectSettings(faker.internet().domainName(),
+ null,
+ null,
+ null,
+ null,
+ licence,
+ new ProjectDonationSettings(false, "d"),
+ keyWords,
+ false,
+ "# Sponsored by " + faker.beer().style() + " " + faker.beer().name());
+ String projectName = normalize(faker.funnyName().name() + "_" + faker.minecraft().animalName());
+ String quote = faker.theItCrowd().quotes();
+ NewProjectForm newProject = new NewProjectForm(settings,
+ Category.values()[faker.random().nextInt(Category.values().length)],
+ quote.substring(0, Math.min(quote.length(), 254)),
+ ownerId,
+ projectName.substring(0, Math.min(projectName.length(), 24)),
+ "# " + projectName + "\n\n" + "> " + faker.leagueOfLegends().quote());
+ ProjectTable projectTable = projectFactory.createProject(newProject);
+
+ projectTable.setVisibility(Visibility.PUBLIC);
+ projectTable.setCreatedAt(faker.date().past(100 * 365, TimeUnit.DAYS).toLocalDateTime().atOffset(ZoneOffset.UTC));
+ projectsDAO.update(projectTable);
+ }
+
+ private String normalize(String input) {
+ return input.replace(" ", "_").replace("\"", "").replace("'", "").replace(".", "");
+ }
+}
diff --git a/frontend/src/lib b/frontend/src/lib
index 4dc74966a..6343bfe7d 160000
--- a/frontend/src/lib
+++ b/frontend/src/lib
@@ -1 +1 @@
-Subproject commit 4dc74966aa6df91a73adb2876662cefc5d5aeb79
+Subproject commit 6343bfe7d2e0bc9cdb2d14b345d0814a3bb431f3
diff --git a/frontend/src/pages/index.vue b/frontend/src/pages/index.vue
index aa930d327..6f6aa01b7 100644
--- a/frontend/src/pages/index.vue
+++ b/frontend/src/pages/index.vue
@@ -52,8 +52,7 @@ const loggedOut = ref("loggedOut" in route.query);
const projects = ref | null>();
const requestParams = computed(() => {
- // TODO change the limit back to something larger
- const limit = 4;
+ const limit = 10;
const params: Record = {
limit: limit,
offset: page.value * limit,