import { chown , copyPath , createUser , deleteUser , ensureConfFilesExists , getEnvironment , getState , getStatus , homePath , isProjectRunning , launchProjectDaemon , mkdir , setupDataPath , } from "./util" ; import { BaseProject , CopyOptions , getProject , ProjectStatus , ProjectState , } from "./base" ; import getLogger from "@cocalc/backend/logger" ; import { getUid } from "@cocalc/backend/misc" ; const winston = getLogger ( "project-control:single-user" ) ; const MAX_START_TIME_MS = 30000 ; const MAX_STOP_TIME_MS = 20000 ; class Project extends BaseProject { private HOME : string ; private uid : number ; constructor ( project_id ) { super ( project_id ) ; this . HOME = homePath ( this . project_id ) ; this . uid = getUid ( this . project_id ) ; } async state ( ) { if ( this . stateChanging != null ) { return this . stateChanging ; } const state = await getState ( this . HOME ) ; winston . debug ( `got state of ${ this . project_id } = ${ JSON . stringify ( state ) }` ) ; this . saveStateToDatabase ( state ) ; return state ; } async status ( ) { const status = await getStatus ( this . HOME ) ; winston . debug ( `got status of ${ this . project_id } = ${ JSON . stringify ( status ) }` ) ; this . saveStatusToDatabase ( status ) ; return status ; } async start ( ) { if ( this . stateChanging != null ) return ; winston . info ( `start ${ this . project_id }` ) ; const HOME = this . HOME ; if ( await isProjectRunning ( HOME ) ) { winston . debug ( "start -- already running" ) ; await this . saveStateToDatabase ( { state : "running" } ) ; return ; } try { this . stateChanging = { state : "starting" } ; await this . saveStateToDatabase ( this . stateChanging ) ; await this . siteLicenseHook ( ) ; await mkdir ( HOME , { recursive : true } ) ; await createUser ( this . project_id ) ; await chown ( HOME , this . uid ) ; await ensureConfFilesExists ( HOME , this . uid ) ; const env = await getEnvironment ( this . project_id ) ; winston . debug ( `start ${ this . project_id }: env = ${ JSON . stringify ( env ) }` ) ; await setupDataPath ( HOME , this . uid ) ; await launchProjectDaemon ( env , this . uid ) ; await this . wait ( { until : async ( ) => { if ( ! ( await isProjectRunning ( this . HOME ) ) ) { return false ; } const status = await this . status ( ) ; return ! ! status . secret_token && ! ! status [ "hub-server.port" ] ; } , maxTime : MAX_START_TIME_MS , } ) ; } finally { this . stateChanging = undefined ; await this . state ( ) ; } } async stop ( ) { if ( this . stateChanging != null ) return ; winston . info ( "stop " , this . project_id ) ; if ( ! ( await isProjectRunning ( this . HOME ) ) ) { await this . saveStateToDatabase ( { state : "opened" } ) ; return ; } try { this . stateChanging = { state : "stopping" } ; await this . saveStateToDatabase ( this . stateChanging ) ; await deleteUser ( this . project_id ) ; await this . wait ( { until : async ( ) => ! ( await isProjectRunning ( this . HOME ) ) , maxTime : MAX_STOP_TIME_MS , } ) ; } finally { this . stateChanging = undefined ; await this . state ( ) ; } } async copyPath ( opts ) { winston . debug ( "copyPath " , this . project_id , opts ) ; await copyPath ( opts , this . project_id , opts . target_project_id ? getUid ( opts . target_project_id ) : undefined ) ; return "" ; } } export default function get ( project_id ) { return ( getProject ( project_id ) as Project ) ?? new Project ( project_id ) ; }