this repo has no description smallweb.run
smallweb
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

add support for running a specific app

pomdtr 520f62c6 800b93c6

+97 -45
+97 -45
cmd/up.go
··· 17 17 "slices" 18 18 "strings" 19 19 "sync" 20 + "syscall" 21 + "unsafe" 20 22 21 23 _ "embed" 22 24 23 25 "github.com/caddyserver/certmagic" 24 26 "github.com/charmbracelet/keygen" 25 27 "github.com/charmbracelet/ssh" 28 + "github.com/creack/pty" 26 29 "github.com/pkg/sftp" 27 30 "github.com/pomdtr/smallweb/app" 28 31 "github.com/pomdtr/smallweb/watcher" ··· 162 165 return false 163 166 }, 164 167 SubsystemHandlers: map[string]ssh.SubsystemHandler{ 165 - "sftp": sftpHandler(sftp.WithServerWorkingDirectory(k.String("dir"))), 168 + "sftp": func(sess ssh.Session) { 169 + var workDir string 170 + if sess.User() == "_" { 171 + workDir = k.String("dir") 172 + } else { 173 + app, err := app.NewApp(sess.User(), k.String("dir"), k.String("domain"), slices.Contains(k.Strings("adminApps"), sess.User())) 174 + if err != nil { 175 + fmt.Fprintf(sess, "failed to load app: %v\n", err) 176 + return 177 + } 178 + 179 + workDir = app.Dir 180 + } 181 + 182 + server, err := sftp.NewServer( 183 + sess, 184 + sftp.WithServerWorkingDirectory(workDir), 185 + ) 186 + 187 + if err != nil { 188 + log.Printf("sftp server init error: %s\n", err) 189 + return 190 + } 191 + if err := server.Serve(); err == io.EOF { 192 + server.Close() 193 + fmt.Println("sftp client exited session.") 194 + } else if err != nil { 195 + fmt.Println("sftp server completed with error:", err) 196 + } 197 + }, 166 198 }, 167 199 Handler: func(sess ssh.Session) { 168 - execPath, err := os.Executable() 169 - if err != nil { 170 - fmt.Fprintf(sess, "failed to get executable path: %v", err) 171 - return 200 + var cmd *exec.Cmd 201 + if sess.User() == "_" { 202 + execPath, err := os.Executable() 203 + if err != nil { 204 + fmt.Fprintf(sess, "failed to get executable path: %v\n", err) 205 + return 206 + } 207 + cmd = exec.Command(execPath, sess.Command()...) 208 + cmd.Env = os.Environ() 209 + } else { 210 + app, err := app.NewApp(sess.User(), k.String("dir"), k.String("domain"), slices.Contains(k.Strings("adminApps"), sess.User())) 211 + if err != nil { 212 + fmt.Fprintf(sess, "failed to load app: %v\n", err) 213 + return 214 + } 215 + 216 + wk := worker.NewWorker(app, k.String("dir"), k.String("domain")) 217 + command, err := wk.Command(sess.Context(), sess.Command()...) 218 + if err != nil { 219 + fmt.Fprintf(sess, "failed to get command: %v\n", err) 220 + return 221 + } 222 + 223 + cmd = command 172 224 } 173 225 174 - cmd := exec.CommandContext(sess.Context(), execPath, sess.Command()...) 175 - stdin, err := cmd.StdinPipe() 176 - if err != nil { 177 - fmt.Fprintf(sess, "failed to get stdin pipe: %v", err) 226 + ptyReq, winCh, isPty := sess.Pty() 227 + if isPty { 228 + cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term)) 229 + f, err := pty.Start(cmd) 230 + if err != nil { 231 + panic(err) 232 + } 233 + go func() { 234 + for win := range winCh { 235 + syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ), 236 + uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(win.Height), uint16(win.Width), 0, 0}))) 237 + } 238 + }() 239 + go func() { 240 + io.Copy(f, sess) 241 + }() 242 + io.Copy(sess, f) 243 + cmd.Wait() 178 244 return 179 - } 245 + } else { 246 + stdin, err := cmd.StdinPipe() 247 + if err != nil { 248 + fmt.Fprintf(sess, "failed to get stdin pipe: %v", err) 249 + return 250 + } 251 + 252 + go func() { 253 + //nolint:errcheck 254 + io.Copy(stdin, sess) 255 + }() 180 256 181 - go func() { 182 - //nolint:errcheck 183 - io.Copy(stdin, sess) 184 - }() 257 + cmd.Stdout = sess 258 + cmd.Stderr = sess.Stderr() 185 259 186 - cmd.Stdout = sess 187 - cmd.Stderr = sess.Stderr() 260 + if err := cmd.Run(); err != nil { 261 + var exitErr *exec.ExitError 262 + if errors.As(err, &exitErr) { 263 + //nolint:errcheck 264 + sess.Exit(exitErr.ExitCode()) 265 + return 266 + } 188 267 189 - if err := cmd.Run(); err != nil { 190 - var exitErr *exec.ExitError 191 - if errors.As(err, &exitErr) { 268 + fmt.Fprintf(sess, "failed to run command: %v", err) 192 269 //nolint:errcheck 193 - sess.Exit(exitErr.ExitCode()) 270 + sess.Exit(1) 194 271 return 195 272 } 196 - 197 - fmt.Fprintf(sess, "failed to run command: %v", err) 198 - //nolint:errcheck 199 - sess.Exit(1) 200 - return 201 273 } 202 274 }, 203 275 } ··· 422 494 423 495 return false, nil 424 496 } 425 - 426 - // SftpHandler handler for SFTP subsystem 427 - func sftpHandler(options ...sftp.ServerOption) func(sess ssh.Session) { 428 - return func(sess ssh.Session) { 429 - server, err := sftp.NewServer( 430 - sess, 431 - options..., 432 - ) 433 - if err != nil { 434 - log.Printf("sftp server init error: %s\n", err) 435 - return 436 - } 437 - if err := server.Serve(); err == io.EOF { 438 - server.Close() 439 - fmt.Println("sftp client exited session.") 440 - } else if err != nil { 441 - fmt.Println("sftp server completed with error:", err) 442 - } 443 - } 444 - }