mirror of
https://github.com/versia-pub/server.git
synced 2025-12-08 09:18:19 +01:00
Add CLI and CLI tests
This commit is contained in:
parent
7dbe5dfecd
commit
5287ceb99e
169
cli.ts
Normal file
169
cli.ts
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { client } from "~database/datasource";
|
||||||
|
import { createNewLocalUser } from "~database/entities/User";
|
||||||
|
|
||||||
|
const args = process.argv;
|
||||||
|
|
||||||
|
const help = `
|
||||||
|
${chalk.bold(`Usage: bun cli <command> ${chalk.blue("[...flags]")} [...args]`)}
|
||||||
|
|
||||||
|
${chalk.bold("Commands:")}
|
||||||
|
${chalk.blue("help")} ${chalk.gray(
|
||||||
|
"................."
|
||||||
|
)} Show this help message
|
||||||
|
${chalk.blue("user")} ${chalk.gray(".................")} Manage users
|
||||||
|
${chalk.blue("create")} ${chalk.gray("...........")} Create a new user
|
||||||
|
${chalk.green("username")} ${chalk.gray(
|
||||||
|
"....."
|
||||||
|
)} Username of the user
|
||||||
|
${chalk.green("password")} ${chalk.gray(
|
||||||
|
"....."
|
||||||
|
)} Password of the user
|
||||||
|
${chalk.green("email")} ${chalk.gray("........")} Email of the user
|
||||||
|
${chalk.yellow("--admin")} ${chalk.gray(
|
||||||
|
"......"
|
||||||
|
)} Make the user an admin (optional)
|
||||||
|
${chalk.bold("Example:")} ${chalk.bgGray(
|
||||||
|
`bun cli user create admin password123 admin@gmail.com --admin`
|
||||||
|
)}
|
||||||
|
${chalk.blue("delete")} ${chalk.gray("...........")} Delete a user
|
||||||
|
${chalk.green("username")} ${chalk.gray(
|
||||||
|
"....."
|
||||||
|
)} Username of the user
|
||||||
|
${chalk.bold("Example:")} ${chalk.bgGray(
|
||||||
|
`bun cli user delete admin`
|
||||||
|
)}
|
||||||
|
${chalk.blue("list")} ${chalk.gray(".............")} List all users
|
||||||
|
${chalk.yellow("--admins")} ${chalk.gray(
|
||||||
|
"....."
|
||||||
|
)} List only admins (optional)
|
||||||
|
${chalk.bold("Example:")} ${chalk.bgGray(`bun cli user list`)}
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (args.length < 3) {
|
||||||
|
console.log(help);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const command = args[2];
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case "help":
|
||||||
|
console.log(help);
|
||||||
|
break;
|
||||||
|
case "user":
|
||||||
|
switch (args[3]) {
|
||||||
|
case "create": {
|
||||||
|
// Check if --admin flag is provided
|
||||||
|
const argsWithFlags = args.filter(arg => arg.startsWith("--"));
|
||||||
|
const argsWithoutFlags = args.filter(
|
||||||
|
arg => !arg.startsWith("--")
|
||||||
|
);
|
||||||
|
|
||||||
|
const username = argsWithoutFlags[4];
|
||||||
|
const password = argsWithoutFlags[5];
|
||||||
|
const email = argsWithoutFlags[6];
|
||||||
|
|
||||||
|
const admin = argsWithFlags.includes("--admin");
|
||||||
|
|
||||||
|
// Check if username, password and email are provided
|
||||||
|
if (!username || !password || !email) {
|
||||||
|
console.log(
|
||||||
|
`${chalk.red(`✗`)} Missing username, password or email`
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user already exists
|
||||||
|
const user = await client.user.findFirst({
|
||||||
|
where: {
|
||||||
|
OR: [{ username }, { email }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
console.log(`${chalk.red(`✗`)} User already exists`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create user
|
||||||
|
const newUser = await createNewLocalUser({
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
username: username,
|
||||||
|
admin: admin,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`${chalk.green(`✓`)} Created user ${chalk.blue(
|
||||||
|
newUser.username
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "delete": {
|
||||||
|
const username = args[4];
|
||||||
|
|
||||||
|
if (!username) {
|
||||||
|
console.log(`${chalk.red(`✗`)} Missing username`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await client.user.findFirst({
|
||||||
|
where: {
|
||||||
|
username: username,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
console.log(`${chalk.red(`✗`)} User not found`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.user.delete({
|
||||||
|
where: {
|
||||||
|
id: user.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`${chalk.green(`✓`)} Deleted user ${chalk.blue(
|
||||||
|
user.username
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "list": {
|
||||||
|
const admins = args.includes("--admins");
|
||||||
|
|
||||||
|
const users = await client.user.findMany({
|
||||||
|
where: {
|
||||||
|
isAdmin: admins || undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`${chalk.green(`✓`)} Found ${chalk.blue(
|
||||||
|
users.length
|
||||||
|
)} users`
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const user of users) {
|
||||||
|
console.log(
|
||||||
|
`\t${chalk.blue(user.username)} ${chalk.gray(
|
||||||
|
user.email
|
||||||
|
)} ${chalk.green(user.isAdmin ? "Admin" : "User")}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
console.log(`Unknown command ${chalk.blue(command)}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log(`Unknown command ${chalk.blue(command)}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
@ -207,6 +207,7 @@ export const createNewLocalUser = async (data: {
|
||||||
bio?: string;
|
bio?: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
header?: string;
|
header?: string;
|
||||||
|
admin?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const config = getConfig();
|
const config = getConfig();
|
||||||
|
|
||||||
|
|
@ -221,6 +222,7 @@ export const createNewLocalUser = async (data: {
|
||||||
note: data.bio ?? "",
|
note: data.bio ?? "",
|
||||||
avatar: data.avatar ?? config.defaults.avatar,
|
avatar: data.avatar ?? config.defaults.avatar,
|
||||||
header: data.header ?? config.defaults.avatar,
|
header: data.header ?? config.defaults.avatar,
|
||||||
|
isAdmin: data.admin ?? false,
|
||||||
uri: "",
|
uri: "",
|
||||||
publicKey: keys.public_key,
|
publicKey: keys.public_key,
|
||||||
privateKey: keys.private_key,
|
privateKey: keys.private_key,
|
||||||
|
|
|
||||||
27
index.ts
27
index.ts
|
|
@ -8,6 +8,8 @@ import "reflect-metadata";
|
||||||
import { AuthData, getFromRequest } from "~database/entities/User";
|
import { AuthData, getFromRequest } from "~database/entities/User";
|
||||||
import { APIRouteMeta } from "~types/api";
|
import { APIRouteMeta } from "~types/api";
|
||||||
import { mkdir } from "fs/promises";
|
import { mkdir } from "fs/promises";
|
||||||
|
import { client } from "~database/datasource";
|
||||||
|
import { PrismaClientInitializationError } from "@prisma/client/runtime/library";
|
||||||
|
|
||||||
const router = new Bun.FileSystemRouter({
|
const router = new Bun.FileSystemRouter({
|
||||||
style: "nextjs",
|
style: "nextjs",
|
||||||
|
|
@ -25,6 +27,20 @@ if (!(await requests_log.exists())) {
|
||||||
await Bun.write(process.cwd() + "/logs/requests.log", "");
|
await Bun.write(process.cwd() + "/logs/requests.log", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if database is reachable
|
||||||
|
const postCount = 0;
|
||||||
|
try {
|
||||||
|
await client.status.count();
|
||||||
|
} catch (e) {
|
||||||
|
const error = e as PrismaClientInitializationError;
|
||||||
|
console.error(
|
||||||
|
`${chalk.red(`✗`)} ${chalk.bold(
|
||||||
|
"Error while connecting to database: "
|
||||||
|
)} ${error.message}`
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
Bun.serve({
|
Bun.serve({
|
||||||
port: config.http.bind_port,
|
port: config.http.bind_port,
|
||||||
hostname: config.http.bind || "0.0.0.0", // defaults to "0.0.0.0"
|
hostname: config.http.bind || "0.0.0.0", // defaults to "0.0.0.0"
|
||||||
|
|
@ -153,3 +169,14 @@ console.log(
|
||||||
)}`
|
)}`
|
||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`${chalk.green(`✓`)} ${chalk.bold(`Database is ${chalk.blue("online")}`)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Print "serving x posts"
|
||||||
|
console.log(
|
||||||
|
`${chalk.green(`✓`)} ${chalk.bold(
|
||||||
|
`Serving ${chalk.blue(postCount)} posts`
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,8 @@
|
||||||
"migrate-dev": "bunx prisma migrate dev",
|
"migrate-dev": "bunx prisma migrate dev",
|
||||||
"migrate": "bunx prisma migrate deploy",
|
"migrate": "bunx prisma migrate deploy",
|
||||||
"lint": "eslint --config .eslintrc.cjs --ext .ts .",
|
"lint": "eslint --config .eslintrc.cjs --ext .ts .",
|
||||||
"generate": "bunx prisma generate"
|
"generate": "bunx prisma generate",
|
||||||
|
"cli": "bun run cli.ts"
|
||||||
},
|
},
|
||||||
"trustedDependencies": [
|
"trustedDependencies": [
|
||||||
"sharp",
|
"sharp",
|
||||||
|
|
|
||||||
94
tests/cli.test.ts
Normal file
94
tests/cli.test.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
||||||
|
import { client } from "~database/datasource";
|
||||||
|
import { createNewLocalUser } from "~database/entities/User";
|
||||||
|
|
||||||
|
describe("cli.ts", () => {
|
||||||
|
describe("User creation", () => {
|
||||||
|
it("should execute user create command without admin flag", async () => {
|
||||||
|
afterAll(async () => {
|
||||||
|
await client.user.deleteMany({
|
||||||
|
where: {
|
||||||
|
username: "testuser297",
|
||||||
|
email: "testuser297@gmail.com",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Run command and wait for it to finish
|
||||||
|
Bun.spawnSync([
|
||||||
|
"bun",
|
||||||
|
"run",
|
||||||
|
"cli.ts",
|
||||||
|
"user",
|
||||||
|
"create",
|
||||||
|
"testuser297",
|
||||||
|
"password123",
|
||||||
|
"testuser297@gmail.com",
|
||||||
|
]);
|
||||||
|
|
||||||
|
const createdUser = await client.user.findFirst({
|
||||||
|
where: {
|
||||||
|
username: "testuser297",
|
||||||
|
email: "testuser297@gmail.com",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createdUser).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should execute user create command with admin flag", async () => {
|
||||||
|
afterAll(async () => {
|
||||||
|
await client.user.deleteMany({
|
||||||
|
where: {
|
||||||
|
username: "testuser297",
|
||||||
|
email: "testuser297@gmail.com",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Run command and wait for it to finish
|
||||||
|
Bun.spawnSync([
|
||||||
|
"bun",
|
||||||
|
"run",
|
||||||
|
"cli.ts",
|
||||||
|
"user",
|
||||||
|
"create",
|
||||||
|
"testuser297",
|
||||||
|
"password123",
|
||||||
|
"testuser297@gmail.com",
|
||||||
|
"--admin",
|
||||||
|
]);
|
||||||
|
|
||||||
|
const createdUser = await client.user.findFirst({
|
||||||
|
where: {
|
||||||
|
username: "testuser297",
|
||||||
|
email: "testuser297@gmail.com",
|
||||||
|
isAdmin: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createdUser).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should execute user delete command", async () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await createNewLocalUser({
|
||||||
|
username: "bob124",
|
||||||
|
password: "jesus",
|
||||||
|
email: "bob124@bob124.com",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Bun.spawnSync(["bun", "run", "cli", "user", "delete", "bob124"]);
|
||||||
|
|
||||||
|
const userExists = await client.user.findFirst({
|
||||||
|
where: {
|
||||||
|
username: "bob124",
|
||||||
|
email: "bob124@bob124.com",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(!!userExists).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue