fixed members login and permissions issues

This commit is contained in:
2026-02-23 00:57:34 +00:00
parent ed0debfeac
commit 97da2e2736
42 changed files with 574 additions and 113 deletions
@@ -52,9 +52,7 @@ RUN curl -fsSL "https://github.com/jesseduffield/lazygit/releases/download/v${LA
RUN npm install -g @mariozechner/pi-coding-agent
RUN mkdir -p /home/officer/Documents /home/officer/Downloads /home/officer/Music /home/officer/Videos /home/officer/Pictures /home/officer/Desktop /home/officer/Projects
WORKDIR /home/officer
WORKDIR /tmp
ENV TERMINAL_PTY_PORT=5337
+6 -5
View File
@@ -17,17 +17,18 @@ fi
# Create group and user
groupadd -g "$USER_GID" "$USERNAME" 2>/dev/null || true
useradd -u "$USER_UID" -g "$USER_GID" -s /bin/zsh -d /home/officer "$USERNAME" 2>/dev/null || true
mkdir -p /home/$USERNAME
useradd -u "$USER_UID" -g "$USER_GID" -s /bin/zsh -d /home/$USERNAME "$USERNAME" 2>/dev/null || true
# Passwordless sudo
echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/terminal-user
chmod 0440 /etc/sudoers.d/terminal-user
# Seed LazyVim config if not present
if [ ! -d /home/officer/.config/nvim ]; then
mkdir -p /home/officer/.config
cp -r /opt/lazyvim-starter /home/officer/.config/nvim
chown -R "$USER_UID:$USER_GID" /home/officer/.config
if [ ! -d /home/$USERNAME/.config/nvim ]; then
mkdir -p /home/$USERNAME/.config
cp -r /opt/lazyvim-starter /home/$USERNAME/.config/nvim
chown -R "$USER_UID:$USER_GID" /home/$USERNAME/.config
fi
# Run sidecar as the user
+12 -12
View File
@@ -6,7 +6,7 @@ import { getHomeDir } from '@@/data-path';
import { syncUserPiConfig } from '@@/api/server-settings/sync-user-pi-config';
import { officerdb, Users } from 'officerdb';
type WSData = { userId: number; email: string; role: string; sandboxed: boolean; sessionId?: string; cwd?: string; cols?: number; rows?: number };
type WSData = { userId: number; email: string; username: string; role: string; sandboxed: boolean; sessionId?: string; cwd?: string; cols?: number; rows?: number };
type ShellInfo = { command: string; args: string[]; name: string };
type BridgeSession = {
client: ServerWebSocket<WSData>;
@@ -101,7 +101,7 @@ const ensureDockerImage = () => {
dockerImageReady = true;
};
const startDockerSidecar = (port: number, homeDir: string, userId: number, email: string): { dockerId: string } => {
const startDockerSidecar = (port: number, homeDir: string, userId: number, username: string): { dockerId: string } => {
ensureDockerImage();
const dockerPath = Bun.which('docker') ?? 'docker';
const dockerId = `officer-terminal-${userId}`;
@@ -112,7 +112,6 @@ const startDockerSidecar = (port: number, homeDir: string, userId: number, email
Bun.spawnSync({ cmd: [dockerPath, 'rm', '-f', dockerId], stdout: 'ignore', stderr: 'ignore' });
}
const username = (email.split('@')[0] ?? 'officer').replace(/[^a-z0-9_-]/g, '_').slice(0, 32);
let uid = 1000;
let gid = 1000;
try {
@@ -123,6 +122,7 @@ const startDockerSidecar = (port: number, homeDir: string, userId: number, email
// fallback to defaults
}
const containerHome = `/home/${username}`;
const run = Bun.spawnSync({
cmd: [
dockerPath,
@@ -145,9 +145,9 @@ const startDockerSidecar = (port: number, homeDir: string, userId: number, email
'-e',
`TERMINAL_GID=${gid}`,
'-v',
`${homeDir}:/home/officer`,
`${homeDir}:${containerHome}`,
'-w',
'/home/officer',
containerHome,
tag,
],
stdout: 'inherit',
@@ -210,7 +210,7 @@ const dockerStart = (dockerId: string) => {
return result.exitCode === 0;
};
const ensureDockerContainer = async (email: string, userId: number, homeDir: string) => {
const ensureDockerContainer = async (email: string, userId: number, homeDir: string, username: string) => {
const map = await loadContainerMap();
const existing = map[email];
if (existing && dockerContainerRunning(existing.dockerId)) return existing;
@@ -221,7 +221,7 @@ const ensureDockerContainer = async (email: string, userId: number, homeDir: str
}
const port = existing?.port ?? getAvailablePort(map, userId);
const docker = startDockerSidecar(port, homeDir, userId, email);
const docker = startDockerSidecar(port, homeDir, userId, username);
const next = { userId, email, dockerId: docker.dockerId, port };
map[email] = next;
await saveContainerMap(map);
@@ -269,7 +269,7 @@ const startHostSidecar = async () => {
export const initTerminalSidecars = async () => {
await startHostSidecar();
ensureDockerImage();
const users = await officerdb.select({ id: Users.id, email: Users.email }).from(Users);
const users = await officerdb.select({ id: Users.id, email: Users.email, username: Users.username }).from(Users);
for (const user of users) {
const homeDir = getHomeDir(user.email);
mkdirSync(dirname(homeDir), { recursive: true });
@@ -278,7 +278,7 @@ export const initTerminalSidecars = async () => {
await syncUserPiConfig(user.email).catch((err) => {
console.error(`[terminal] failed to sync Pi config for ${user.email}:`, err);
});
await ensureDockerContainer(user.email, user.id, homeDir);
await ensureDockerContainer(user.email, user.id, homeDir, user.username ?? user.email.split('@')[0]!);
console.log(`[terminal] sidecar ready for ${user.email}`);
} catch (err) {
console.error(`[terminal] failed to start sidecar for ${user.email}:`, err);
@@ -287,7 +287,6 @@ export const initTerminalSidecars = async () => {
};
const containerShell: ShellInfo = { command: '/bin/zsh', args: ['-d', '-i'], name: 'zsh' };
const containerHome = '/home/officer';
const resolveCwd = (home: string, cwd?: string) => {
if (!cwd || cwd === '~') return home;
@@ -298,7 +297,7 @@ const resolveCwd = (home: string, cwd?: string) => {
export const terminalWebsocket = {
async open(ws: ServerWebSocket<WSData>) {
const { email, role, sandboxed } = ws.data;
const { email, username, role, sandboxed } = ws.data;
if (!sandboxed && role !== 'Super Admin') {
sendOutput(ws, '\r\n[Permission denied] Host terminal requires Super Admin role.\r\n');
@@ -363,7 +362,7 @@ export const terminalWebsocket = {
let sidecar: WebSocket | null = null;
let info: ContainerInfo | undefined;
try {
info = await ensureDockerContainer(email, ws.data.userId, cwd);
info = await ensureDockerContainer(email, ws.data.userId, cwd, username);
sidecar = await connectSidecar(info.port);
} catch (err) {
const message = err instanceof Error ? err.message : 'Failed to connect terminal sidecar';
@@ -396,6 +395,7 @@ export const terminalWebsocket = {
}
});
const containerHome = `/home/${username}`;
sidecar.send(
JSON.stringify({
type: 'init',