Run computers with resources and health checks, add settings for bots, workspaces, computers and users
- Computers get CPUs and memory (default 1 CPU, 2 GB), set at creation and editable; new resources stop, remove and recreate the container, its home dir kept. Every computer container has a docker health check and stays "creating" until it passes; only healthy computers are reached, by hostname, else by their IP on the network (dev outside docker). - Docker is required: the app creates the cobble network on startup and exits without docker. - Production container: deploy/run-app-container.sh mounts .env, the data dir and the docker socket; the database (DB_DIR), media, computers' homes and bun-sqlite's live schema now live on the data volume. - Settings pages for bots, workspaces, computers and users, sharing list components; bots can be deleted; users are created with a password and confirmation, edited and deleted through RBAC-gated APIs. - User types trimmed to super_admin, admin, member, read_only and revoked; admins manage members. UserAvatar shows profile photos from the user row. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -1,8 +1,27 @@
|
||||
import { z } from "zod";
|
||||
import { AppData } from "@/src/data/app-data";
|
||||
|
||||
const ComputerCpusSchema = z.coerce
|
||||
.number()
|
||||
.min(AppData.ComputerMinCpus, `Give it at least ${AppData.ComputerMinCpus} CPU`)
|
||||
.max(AppData.ComputerMaxCpus, `Keep it under ${AppData.ComputerMaxCpus} CPUs`);
|
||||
|
||||
const ComputerMemoryMbSchema = z.coerce
|
||||
.number()
|
||||
.int()
|
||||
.min(AppData.ComputerMinMemoryMb, `Give it at least ${AppData.ComputerMinMemoryMb} MB of memory`);
|
||||
|
||||
const ComputerNameSchema = z.string().trim().min(1, "Give the computer a name").max(60, "Keep the name under 60 characters");
|
||||
const ComputerDescriptionSchema = z.string().trim().max(240, "Keep the description short");
|
||||
|
||||
export const CreateComputerRequestSchema = z.object({
|
||||
name: z.string().trim().min(1, "Give the computer a name").max(60, "Keep the name under 60 characters"),
|
||||
description: z.string().trim().max(240, "Keep the description short").default(""),
|
||||
name: ComputerNameSchema,
|
||||
description: ComputerDescriptionSchema.default(""),
|
||||
/**
|
||||
* Defaults to `AppData.ComputerDefaultCpus` / `ComputerDefaultMemoryMb`.
|
||||
*/
|
||||
cpus: ComputerCpusSchema.optional(),
|
||||
memory_mb: ComputerMemoryMbSchema.optional(),
|
||||
/**
|
||||
* Also link it to this workspace.
|
||||
*/
|
||||
@@ -16,3 +35,12 @@ export const ComputerIdRequestSchema = z.object({
|
||||
});
|
||||
|
||||
export type ComputerIdRequest = z.infer<typeof ComputerIdRequestSchema>;
|
||||
|
||||
export const UpdateComputerRequestSchema = ComputerIdRequestSchema.extend({
|
||||
name: ComputerNameSchema.optional(),
|
||||
description: ComputerDescriptionSchema.optional(),
|
||||
cpus: ComputerCpusSchema.optional(),
|
||||
memory_mb: ComputerMemoryMbSchema.optional(),
|
||||
});
|
||||
|
||||
export type UpdateComputerRequest = z.infer<typeof UpdateComputerRequestSchema>;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { z } from "zod";
|
||||
import { UserTypes } from "@/src/dict/user-types-dict";
|
||||
|
||||
export const SaveUserRequestSchema = z
|
||||
.object({
|
||||
/**
|
||||
* Updates this user; creates one when not given.
|
||||
*/
|
||||
user_id: z.coerce.number().int().positive().nullish(),
|
||||
first_name: z.string().trim().min(1, "Enter their first name").max(100),
|
||||
last_name: z.string().trim().max(100).default(""),
|
||||
email: z.email("Enter a valid email address"),
|
||||
username: z.string().trim().min(3, "Usernames must be at least 3 characters").max(32, "Usernames must be at most 32 characters"),
|
||||
/**
|
||||
* Required for a new user; on an existing one, empty keeps theirs.
|
||||
*/
|
||||
password: z.string().max(128).default(""),
|
||||
confirm_password: z.string().max(128).default(""),
|
||||
/**
|
||||
* Every user type they should hold (`UserTypes`). Types the actor may
|
||||
* not grant or remove (`super_admin`, the assignment matrix) are left
|
||||
* as they are.
|
||||
*/
|
||||
user_types: z.array(z.enum(UserTypes.map((type) => type.value))).default([]),
|
||||
})
|
||||
.refine((data) => data.password == data.confirm_password, {
|
||||
message: "The passwords don't match",
|
||||
path: ["confirm_password"],
|
||||
});
|
||||
|
||||
export type SaveUserRequest = z.infer<typeof SaveUserRequestSchema>;
|
||||
|
||||
export const UserIdRequestSchema = z.object({
|
||||
user_id: z.coerce.number().int().positive("Pick a user"),
|
||||
});
|
||||
|
||||
export type UserIdRequest = z.infer<typeof UserIdRequestSchema>;
|
||||
Reference in New Issue
Block a user