Add ability to quickly create test users in development (#3764)
This commit is contained in:
@@ -1,32 +0,0 @@
|
||||
import { ToolsIcon, TrashIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import stores from "~/stores";
|
||||
import { createAction } from "~/actions";
|
||||
import { DebugSection } from "~/actions/sections";
|
||||
import env from "~/env";
|
||||
import { deleteAllDatabases } from "~/utils/developer";
|
||||
|
||||
export const clearIndexedDB = createAction({
|
||||
name: ({ t }) => t("Delete IndexedDB cache"),
|
||||
icon: <TrashIcon />,
|
||||
keywords: "cache clear database",
|
||||
section: DebugSection,
|
||||
perform: async ({ t }) => {
|
||||
await deleteAllDatabases();
|
||||
stores.toasts.showToast(t("IndexedDB cache deleted"));
|
||||
},
|
||||
});
|
||||
|
||||
export const development = createAction({
|
||||
name: ({ t }) => t("Development"),
|
||||
keywords: "debug",
|
||||
icon: <ToolsIcon />,
|
||||
iconInContextMenu: false,
|
||||
section: DebugSection,
|
||||
visible: ({ event }) =>
|
||||
env.ENVIRONMENT === "development" ||
|
||||
(event instanceof KeyboardEvent && event.altKey),
|
||||
children: [clearIndexedDB],
|
||||
});
|
||||
|
||||
export const rootDebugActions = [development];
|
||||
50
app/actions/definitions/developer.tsx
Normal file
50
app/actions/definitions/developer.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { ToolsIcon, TrashIcon, UserIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import stores from "~/stores";
|
||||
import { createAction } from "~/actions";
|
||||
import { DeveloperSection } from "~/actions/sections";
|
||||
import env from "~/env";
|
||||
import { client } from "~/utils/ApiClient";
|
||||
import { deleteAllDatabases } from "~/utils/developer";
|
||||
|
||||
export const clearIndexedDB = createAction({
|
||||
name: ({ t }) => t("Delete IndexedDB cache"),
|
||||
icon: <TrashIcon />,
|
||||
keywords: "cache clear database",
|
||||
section: DeveloperSection,
|
||||
perform: async ({ t }) => {
|
||||
await deleteAllDatabases();
|
||||
stores.toasts.showToast(t("IndexedDB cache deleted"));
|
||||
},
|
||||
});
|
||||
|
||||
export const createTestUsers = createAction({
|
||||
name: "Create test users",
|
||||
icon: <UserIcon />,
|
||||
section: DeveloperSection,
|
||||
visible: () => env.ENVIRONMENT === "development",
|
||||
perform: async () => {
|
||||
const count = 10;
|
||||
|
||||
try {
|
||||
await client.post("/developer.create_test_users", { count });
|
||||
stores.toasts.showToast(`${count} test users created`);
|
||||
} catch (err) {
|
||||
stores.toasts.showToast(err.message, { type: "error" });
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const developer = createAction({
|
||||
name: ({ t }) => t("Developer"),
|
||||
keywords: "debug",
|
||||
icon: <ToolsIcon />,
|
||||
iconInContextMenu: false,
|
||||
section: DeveloperSection,
|
||||
visible: ({ event }) =>
|
||||
env.ENVIRONMENT === "development" ||
|
||||
(event instanceof KeyboardEvent && event.altKey),
|
||||
children: [clearIndexedDB, createTestUsers],
|
||||
});
|
||||
|
||||
export const rootDeveloperActions = [developer];
|
||||
@@ -1,5 +1,5 @@
|
||||
import { rootCollectionActions } from "./definitions/collections";
|
||||
import { rootDebugActions } from "./definitions/debug";
|
||||
import { rootDeveloperActions } from "./definitions/developer";
|
||||
import { rootDocumentActions } from "./definitions/documents";
|
||||
import { rootNavigationActions } from "./definitions/navigation";
|
||||
import { rootSettingsActions } from "./definitions/settings";
|
||||
@@ -11,5 +11,5 @@ export default [
|
||||
...rootUserActions,
|
||||
...rootNavigationActions,
|
||||
...rootSettingsActions,
|
||||
...rootDebugActions,
|
||||
...rootDeveloperActions,
|
||||
];
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ActionContext } from "~/types";
|
||||
|
||||
export const CollectionSection = ({ t }: ActionContext) => t("Collection");
|
||||
|
||||
export const DebugSection = ({ t }: ActionContext) => t("Debug");
|
||||
export const DeveloperSection = ({ t }: ActionContext) => t("Debug");
|
||||
|
||||
export const DocumentSection = ({ t }: ActionContext) => t("Document");
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import Logger from "@server/logging/Logger";
|
||||
import { User, Event, Team } from "@server/models";
|
||||
import { UserFlag } from "@server/models/User";
|
||||
|
||||
type Invite = {
|
||||
export type Invite = {
|
||||
name: string;
|
||||
email: string;
|
||||
role: Role;
|
||||
|
||||
58
server/routes/api/developer.ts
Normal file
58
server/routes/api/developer.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Context, Next } from "koa";
|
||||
import Router from "koa-router";
|
||||
import randomstring from "randomstring";
|
||||
import userInviter, { Invite } from "@server/commands/userInviter";
|
||||
import env from "@server/env";
|
||||
import Logger from "@server/logging/Logger";
|
||||
import auth from "@server/middlewares/authentication";
|
||||
import { presentUser } from "@server/presenters";
|
||||
|
||||
const router = new Router();
|
||||
|
||||
function dev() {
|
||||
return async function checkDevelopmentMiddleware(ctx: Context, next: Next) {
|
||||
if (env.ENVIRONMENT !== "development") {
|
||||
throw new Error("Attempted to access development route in production");
|
||||
}
|
||||
|
||||
return next();
|
||||
};
|
||||
}
|
||||
|
||||
router.post("developer.create_test_users", dev(), auth(), async (ctx) => {
|
||||
const { count = 10 } = ctx.body;
|
||||
const { user } = ctx.state;
|
||||
const invites = Array(count)
|
||||
.fill(0)
|
||||
.map(() => {
|
||||
const rando = randomstring.generate(10);
|
||||
|
||||
return {
|
||||
email: `${rando}@example.com`,
|
||||
name: `${rando.slice(0, 5)} Tester`,
|
||||
role: "member",
|
||||
} as Invite;
|
||||
});
|
||||
|
||||
Logger.info("utils", `Creating ${count} test users`, invites);
|
||||
|
||||
// Generate a bunch of invites
|
||||
const response = await userInviter({
|
||||
user,
|
||||
invites,
|
||||
ip: ctx.request.ip,
|
||||
});
|
||||
|
||||
// Convert from invites to active users by marking as active
|
||||
await Promise.all(
|
||||
response.users.map((user) => user.updateActiveAt(ctx.request.ip, true))
|
||||
);
|
||||
|
||||
ctx.body = {
|
||||
data: {
|
||||
users: response.users.map((user) => presentUser(user)),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -1,6 +1,7 @@
|
||||
import Koa from "koa";
|
||||
import bodyParser from "koa-body";
|
||||
import Router from "koa-router";
|
||||
import env from "@server/env";
|
||||
import { NotFoundError } from "@server/errors";
|
||||
import errorHandling from "@server/middlewares/errorHandling";
|
||||
import methodOverride from "@server/middlewares/methodOverride";
|
||||
@@ -10,6 +11,7 @@ import auth from "./auth";
|
||||
import authenticationProviders from "./authenticationProviders";
|
||||
import collections from "./collections";
|
||||
import utils from "./cron";
|
||||
import developer from "./developer";
|
||||
import documents from "./documents";
|
||||
import events from "./events";
|
||||
import fileOperationsRoute from "./fileOperations";
|
||||
@@ -70,6 +72,10 @@ router.use("/", groups.routes());
|
||||
router.use("/", fileOperationsRoute.routes());
|
||||
router.use("/", webhookSubscriptions.routes());
|
||||
|
||||
if (env.ENVIRONMENT === "development") {
|
||||
router.use("/", developer.routes());
|
||||
}
|
||||
|
||||
router.post("*", (ctx) => {
|
||||
ctx.throw(NotFoundError("Endpoint not found"));
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"Unstar": "Unstar",
|
||||
"Delete IndexedDB cache": "Delete IndexedDB cache",
|
||||
"IndexedDB cache deleted": "IndexedDB cache deleted",
|
||||
"Development": "Development",
|
||||
"Developer": "Developer",
|
||||
"Open document": "Open document",
|
||||
"New document": "New document",
|
||||
"Download": "Download",
|
||||
|
||||
Reference in New Issue
Block a user