mirror of
https://github.com/OliveTin/OliveTin
synced 2025-12-13 17:45:36 +00:00
596 lines
18 KiB
Go
596 lines
18 KiB
Go
package api
|
|
|
|
import (
|
|
ctx "context"
|
|
|
|
"connectrpc.com/connect"
|
|
|
|
apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
|
|
apiv1connect "github.com/OliveTin/OliveTin/gen/olivetin/api/v1/apiv1connect"
|
|
"github.com/google/uuid"
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"fmt"
|
|
"net/http"
|
|
|
|
acl "github.com/OliveTin/OliveTin/internal/acl"
|
|
config "github.com/OliveTin/OliveTin/internal/config"
|
|
executor "github.com/OliveTin/OliveTin/internal/executor"
|
|
installationinfo "github.com/OliveTin/OliveTin/internal/installationinfo"
|
|
sv "github.com/OliveTin/OliveTin/internal/stringvariables"
|
|
)
|
|
|
|
type oliveTinAPI struct {
|
|
executor *executor.Executor
|
|
cfg *config.Config
|
|
|
|
connectedClients []*connectedClients
|
|
}
|
|
|
|
type connectedClients struct {
|
|
channel chan *apiv1.EventStreamResponse
|
|
AuthenticatedUser *acl.AuthenticatedUser
|
|
}
|
|
|
|
func (api *oliveTinAPI) KillAction(ctx ctx.Context, req *connect.Request[apiv1.KillActionRequest]) (*connect.Response[apiv1.KillActionResponse], error) {
|
|
ret := &apiv1.KillActionResponse{
|
|
ExecutionTrackingId: req.Msg.ExecutionTrackingId,
|
|
}
|
|
|
|
var execReqLogEntry *executor.InternalLogEntry
|
|
|
|
execReqLogEntry, ret.Found = api.executor.GetLog(req.Msg.ExecutionTrackingId)
|
|
|
|
if !ret.Found {
|
|
log.Warnf("Killing execution request not possible - not found by tracking ID: %v", req.Msg.ExecutionTrackingId)
|
|
return connect.NewResponse(ret), nil
|
|
}
|
|
|
|
log.Warnf("Killing execution request by tracking ID: %v", req.Msg.ExecutionTrackingId)
|
|
|
|
action := api.cfg.FindAction(execReqLogEntry.ActionTitle)
|
|
|
|
if action == nil {
|
|
log.Warnf("Killing execution request not possible - action not found: %v", execReqLogEntry.ActionTitle)
|
|
ret.Killed = false
|
|
return connect.NewResponse(ret), nil
|
|
}
|
|
|
|
user := acl.UserFromContext(ctx, api.cfg)
|
|
|
|
api.killActionByTrackingId(user, action, execReqLogEntry, ret)
|
|
|
|
return connect.NewResponse(ret), nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) killActionByTrackingId(user *acl.AuthenticatedUser, action *config.Action, execReqLogEntry *executor.InternalLogEntry, ret *apiv1.KillActionResponse) {
|
|
if !acl.IsAllowedKill(api.cfg, user, action) {
|
|
log.Warnf("Killing execution request not possible - user not allowed to kill this action: %v", execReqLogEntry.ExecutionTrackingID)
|
|
ret.Killed = false
|
|
}
|
|
|
|
err := api.executor.Kill(execReqLogEntry)
|
|
|
|
if err != nil {
|
|
log.Warnf("Killing execution request err: %v", err)
|
|
ret.AlreadyCompleted = true
|
|
ret.Killed = false
|
|
} else {
|
|
ret.Killed = true
|
|
}
|
|
}
|
|
|
|
func (api *oliveTinAPI) StartAction(ctx ctx.Context, req *connect.Request[apiv1.StartActionRequest]) (*connect.Response[apiv1.StartActionResponse], error) {
|
|
args := make(map[string]string)
|
|
|
|
for _, arg := range req.Msg.Arguments {
|
|
args[arg.Name] = arg.Value
|
|
}
|
|
|
|
api.executor.MapActionIdToBindingLock.RLock()
|
|
pair := api.executor.MapActionIdToBinding[req.Msg.ActionId]
|
|
api.executor.MapActionIdToBindingLock.RUnlock()
|
|
|
|
if pair == nil || pair.Action == nil {
|
|
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("action with ID %s not found", req.Msg.ActionId))
|
|
}
|
|
|
|
authenticatedUser := acl.UserFromContext(ctx, api.cfg)
|
|
|
|
execReq := executor.ExecutionRequest{
|
|
Action: pair.Action,
|
|
EntityPrefix: pair.EntityPrefix,
|
|
TrackingID: req.Msg.UniqueTrackingId,
|
|
Arguments: args,
|
|
AuthenticatedUser: authenticatedUser,
|
|
Cfg: api.cfg,
|
|
}
|
|
|
|
api.executor.ExecRequest(&execReq)
|
|
|
|
ret := &apiv1.StartActionResponse{
|
|
ExecutionTrackingId: execReq.TrackingID,
|
|
}
|
|
|
|
return connect.NewResponse(ret), nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) PasswordHash(ctx ctx.Context, req *connect.Request[apiv1.PasswordHashRequest]) (*connect.Response[apiv1.PasswordHashResponse], error) {
|
|
hash, err := createHash(req.Msg.Password)
|
|
|
|
if err != nil {
|
|
return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("error creating hash: %w", err))
|
|
}
|
|
|
|
ret := &apiv1.PasswordHashResponse{
|
|
Hash: hash,
|
|
}
|
|
|
|
return connect.NewResponse(ret), nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) LocalUserLogin(ctx ctx.Context, req *connect.Request[apiv1.LocalUserLoginRequest]) (*connect.Response[apiv1.LocalUserLoginResponse], error) {
|
|
match := checkUserPassword(api.cfg, req.Msg.Username, req.Msg.Password)
|
|
|
|
if match {
|
|
//grpc.SendHeader(ctx, metadata.Pairs("set-username", req.Username))
|
|
|
|
log.WithFields(log.Fields{
|
|
"username": req.Msg.Username,
|
|
}).Info("LocalUserLogin: User logged in successfully.")
|
|
} else {
|
|
log.WithFields(log.Fields{
|
|
"username": req.Msg.Username,
|
|
}).Warn("LocalUserLogin: User login failed.")
|
|
}
|
|
|
|
return connect.NewResponse(&apiv1.LocalUserLoginResponse{
|
|
Success: match,
|
|
}), nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) StartActionAndWait(ctx ctx.Context, req *connect.Request[apiv1.StartActionAndWaitRequest]) (*connect.Response[apiv1.StartActionAndWaitResponse], error) {
|
|
args := make(map[string]string)
|
|
|
|
for _, arg := range req.Msg.Arguments {
|
|
args[arg.Name] = arg.Value
|
|
}
|
|
|
|
user := acl.UserFromContext(ctx, api.cfg)
|
|
|
|
execReq := executor.ExecutionRequest{
|
|
Action: api.executor.FindActionBindingByID(req.Msg.ActionId),
|
|
TrackingID: uuid.NewString(),
|
|
Arguments: args,
|
|
AuthenticatedUser: user,
|
|
Cfg: api.cfg,
|
|
}
|
|
|
|
wg, _ := api.executor.ExecRequest(&execReq)
|
|
wg.Wait()
|
|
|
|
internalLogEntry, ok := api.executor.GetLog(execReq.TrackingID)
|
|
|
|
if ok {
|
|
return connect.NewResponse(&apiv1.StartActionAndWaitResponse{
|
|
LogEntry: api.internalLogEntryToPb(internalLogEntry, user),
|
|
}), nil
|
|
} else {
|
|
return nil, fmt.Errorf("execution not found")
|
|
}
|
|
}
|
|
|
|
func (api *oliveTinAPI) StartActionByGet(ctx ctx.Context, req *connect.Request[apiv1.StartActionByGetRequest]) (*connect.Response[apiv1.StartActionByGetResponse], error) {
|
|
args := make(map[string]string)
|
|
|
|
execReq := executor.ExecutionRequest{
|
|
Action: api.executor.FindActionBindingByID(req.Msg.ActionId),
|
|
TrackingID: uuid.NewString(),
|
|
Arguments: args,
|
|
AuthenticatedUser: acl.UserFromContext(ctx, api.cfg),
|
|
Cfg: api.cfg,
|
|
}
|
|
|
|
_, uniqueTrackingId := api.executor.ExecRequest(&execReq)
|
|
|
|
return connect.NewResponse(&apiv1.StartActionByGetResponse{
|
|
ExecutionTrackingId: uniqueTrackingId,
|
|
}), nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) StartActionByGetAndWait(ctx ctx.Context, req *connect.Request[apiv1.StartActionByGetAndWaitRequest]) (*connect.Response[apiv1.StartActionByGetAndWaitResponse], error) {
|
|
args := make(map[string]string)
|
|
|
|
user := acl.UserFromContext(ctx, api.cfg)
|
|
|
|
execReq := executor.ExecutionRequest{
|
|
Action: api.executor.FindActionBindingByID(req.Msg.ActionId),
|
|
TrackingID: uuid.NewString(),
|
|
Arguments: args,
|
|
AuthenticatedUser: user,
|
|
Cfg: api.cfg,
|
|
}
|
|
|
|
wg, _ := api.executor.ExecRequest(&execReq)
|
|
wg.Wait()
|
|
|
|
internalLogEntry, ok := api.executor.GetLog(execReq.TrackingID)
|
|
|
|
if ok {
|
|
return connect.NewResponse(&apiv1.StartActionByGetAndWaitResponse{
|
|
LogEntry: api.internalLogEntryToPb(internalLogEntry, user),
|
|
}), nil
|
|
} else {
|
|
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("execution not found"))
|
|
}
|
|
}
|
|
|
|
func (api *oliveTinAPI) internalLogEntryToPb(logEntry *executor.InternalLogEntry, authenticatedUser *acl.AuthenticatedUser) *apiv1.LogEntry {
|
|
pble := &apiv1.LogEntry{
|
|
ActionTitle: logEntry.ActionTitle,
|
|
ActionIcon: logEntry.ActionIcon,
|
|
ActionId: logEntry.ActionId,
|
|
DatetimeStarted: logEntry.DatetimeStarted.Format("2006-01-02 15:04:05"),
|
|
DatetimeFinished: logEntry.DatetimeFinished.Format("2006-01-02 15:04:05"),
|
|
DatetimeIndex: logEntry.Index,
|
|
Output: logEntry.Output,
|
|
TimedOut: logEntry.TimedOut,
|
|
Blocked: logEntry.Blocked,
|
|
ExitCode: logEntry.ExitCode,
|
|
Tags: logEntry.Tags,
|
|
ExecutionTrackingId: logEntry.ExecutionTrackingID,
|
|
ExecutionStarted: logEntry.ExecutionStarted,
|
|
ExecutionFinished: logEntry.ExecutionFinished,
|
|
User: logEntry.Username,
|
|
}
|
|
|
|
if !pble.ExecutionFinished {
|
|
pble.CanKill = acl.IsAllowedKill(api.cfg, authenticatedUser, api.cfg.FindAction(logEntry.ActionConfigTitle))
|
|
}
|
|
|
|
return pble
|
|
}
|
|
|
|
func getExecutionStatusByTrackingID(api *oliveTinAPI, executionTrackingId string) *executor.InternalLogEntry {
|
|
logEntry, ok := api.executor.GetLog(executionTrackingId)
|
|
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
return logEntry
|
|
}
|
|
|
|
func getMostRecentExecutionStatusById(api *oliveTinAPI, actionId string) *executor.InternalLogEntry {
|
|
var ile *executor.InternalLogEntry
|
|
|
|
logs := api.executor.GetLogsByActionId(actionId)
|
|
|
|
if len(logs) == 0 {
|
|
return nil
|
|
} else {
|
|
// Get last log entry
|
|
ile = logs[len(logs)-1]
|
|
}
|
|
|
|
return ile
|
|
}
|
|
|
|
func (api *oliveTinAPI) ExecutionStatus(ctx ctx.Context, req *connect.Request[apiv1.ExecutionStatusRequest]) (*connect.Response[apiv1.ExecutionStatusResponse], error) {
|
|
res := &apiv1.ExecutionStatusResponse{}
|
|
|
|
user := acl.UserFromContext(ctx, api.cfg)
|
|
|
|
var ile *executor.InternalLogEntry
|
|
|
|
if req.Msg.ExecutionTrackingId != "" {
|
|
ile = getExecutionStatusByTrackingID(api, req.Msg.ExecutionTrackingId)
|
|
|
|
} else {
|
|
ile = getMostRecentExecutionStatusById(api, req.Msg.ActionId)
|
|
}
|
|
|
|
if ile == nil {
|
|
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("execution not found for tracking ID %s or action ID %s", req.Msg.ExecutionTrackingId, req.Msg.ActionId))
|
|
} else {
|
|
res.LogEntry = api.internalLogEntryToPb(ile, user)
|
|
}
|
|
|
|
return connect.NewResponse(res), nil
|
|
}
|
|
|
|
/**
|
|
func (api *oliveTinAPI) WatchExecution(req *apiv1.WatchExecutionRequest, srv apiv1.OliveTinApi_WatchExecutionServer) error {
|
|
log.Infof("Watch")
|
|
|
|
if logEntry, ok := api.executor.Logs[req.ExecutionUuid]; !ok {
|
|
log.Errorf("Execution not found: %v", req.ExecutionUuid)
|
|
|
|
return nil
|
|
} else {
|
|
if logEntry.ExecutionStarted {
|
|
for !logEntry.ExecutionCompleted {
|
|
tmp := make([]byte, 256)
|
|
|
|
red, err := io.ReadAtLeast(logEntry.StdoutBuffer, tmp, 1)
|
|
|
|
log.Infof("%v %v", red, err)
|
|
|
|
srv.Send(&apiv1.WatchExecutionUpdate{
|
|
Update: string(tmp),
|
|
})
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
*/
|
|
|
|
func (api *oliveTinAPI) Logout(ctx ctx.Context, req *connect.Request[apiv1.LogoutRequest]) (*connect.Response[apiv1.LogoutResponse], error) {
|
|
//user := acl.UserFromContext(ctx, cfg)
|
|
|
|
//grpc.SendHeader(ctx, metadata.Pairs("logout-provider", user.Provider))
|
|
//grpc.SendHeader(ctx, metadata.Pairs("logout-sid", user.SID))
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) GetDashboardComponents(ctx ctx.Context, req *connect.Request[apiv1.GetDashboardComponentsRequest]) (*connect.Response[apiv1.GetDashboardComponentsResponse], error) {
|
|
user := acl.UserFromContext(ctx, api.cfg)
|
|
|
|
if user.IsGuest() && api.cfg.AuthRequireGuestsToLogin {
|
|
return nil, connect.NewError(connect.CodePermissionDenied, fmt.Errorf("guests are not allowed to access the dashboard"))
|
|
}
|
|
|
|
res := buildDashboardResponse(api.executor, api.cfg, user)
|
|
|
|
/*
|
|
if len(res.Actions) == 0 {
|
|
log.WithFields(log.Fields{
|
|
"username": user.Username,
|
|
"usergroupLine": user.UsergroupLine,
|
|
"provider": user.Provider,
|
|
"acls": user.Acls,
|
|
"availableActions": len(api.cfg.Actions),
|
|
}).Warn("Zero actions found for user")
|
|
}
|
|
*/
|
|
|
|
log.Tracef("GetDashboardComponents: %v", res)
|
|
|
|
return connect.NewResponse(res), nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) GetLogs(ctx ctx.Context, req *connect.Request[apiv1.GetLogsRequest]) (*connect.Response[apiv1.GetLogsResponse], error) {
|
|
user := acl.UserFromContext(ctx, api.cfg)
|
|
|
|
ret := &apiv1.GetLogsResponse{}
|
|
|
|
logEntries, countRemaining := api.executor.GetLogTrackingIds(req.Msg.StartOffset, api.cfg.LogHistoryPageSize)
|
|
|
|
for _, logEntry := range logEntries {
|
|
action := api.cfg.FindAction(logEntry.ActionTitle)
|
|
|
|
if action == nil || acl.IsAllowedLogs(api.cfg, user, action) {
|
|
pbLogEntry := api.internalLogEntryToPb(logEntry, user)
|
|
|
|
ret.Logs = append(ret.Logs, pbLogEntry)
|
|
}
|
|
}
|
|
|
|
ret.CountRemaining = countRemaining
|
|
ret.PageSize = api.cfg.LogHistoryPageSize
|
|
|
|
return connect.NewResponse(ret), nil
|
|
}
|
|
|
|
/*
|
|
This function is ONLY a helper for the UI - the arguments are validated properly
|
|
on the StartAction -> Executor chain. This is here basically to provide helpful
|
|
error messages more quickly before starting the action.
|
|
*/
|
|
func (api *oliveTinAPI) ValidateArgumentType(ctx ctx.Context, req *connect.Request[apiv1.ValidateArgumentTypeRequest]) (*connect.Response[apiv1.ValidateArgumentTypeResponse], error) {
|
|
err := executor.TypeSafetyCheck("", req.Msg.Value, req.Msg.Type)
|
|
desc := ""
|
|
|
|
if err != nil {
|
|
desc = err.Error()
|
|
}
|
|
|
|
return connect.NewResponse(&apiv1.ValidateArgumentTypeResponse{
|
|
Valid: err == nil,
|
|
Description: desc,
|
|
}), nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) WhoAmI(ctx ctx.Context, req *connect.Request[apiv1.WhoAmIRequest]) (*connect.Response[apiv1.WhoAmIResponse], error) {
|
|
user := acl.UserFromContext(ctx, api.cfg)
|
|
|
|
res := &apiv1.WhoAmIResponse{
|
|
AuthenticatedUser: user.Username,
|
|
Usergroup: user.UsergroupLine,
|
|
Provider: user.Provider,
|
|
Sid: user.SID,
|
|
Acls: user.Acls,
|
|
}
|
|
|
|
return connect.NewResponse(res), nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) SosReport(ctx ctx.Context, req *connect.Request[apiv1.SosReportRequest]) (*connect.Response[apiv1.SosReportResponse], error) {
|
|
sos := installationinfo.GetSosReport()
|
|
|
|
if !api.cfg.InsecureAllowDumpSos {
|
|
log.Info(sos)
|
|
sos = "Your SOS Report has been logged to OliveTin logs.\n\nIf you are in a safe network, you can temporarily set `insecureAllowDumpSos: true` in your config.yaml, restart OliveTin, and refresh this page - it will put the output directly in the browser."
|
|
}
|
|
|
|
ret := &apiv1.SosReportResponse{
|
|
Alert: sos,
|
|
}
|
|
|
|
return connect.NewResponse(ret), nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) DumpVars(ctx ctx.Context, req *connect.Request[apiv1.DumpVarsRequest]) (*connect.Response[apiv1.DumpVarsResponse], error) {
|
|
res := &apiv1.DumpVarsResponse{}
|
|
|
|
if !api.cfg.InsecureAllowDumpVars {
|
|
res.Alert = "Dumping variables is not allowed by default because it is insecure."
|
|
|
|
return connect.NewResponse(res), nil
|
|
}
|
|
|
|
res.Alert = "Dumping variables has been enabled in the configuration. Please set InsecureAllowDumpVars = false again after you don't need it anymore"
|
|
res.Contents = sv.GetAll()
|
|
|
|
return connect.NewResponse(res), nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) DumpPublicIdActionMap(ctx ctx.Context, req *connect.Request[apiv1.DumpPublicIdActionMapRequest]) (*connect.Response[apiv1.DumpPublicIdActionMapResponse], error) {
|
|
res := &apiv1.DumpPublicIdActionMapResponse{}
|
|
res.Contents = make(map[string]*apiv1.ActionEntityPair)
|
|
|
|
if !api.cfg.InsecureAllowDumpActionMap {
|
|
res.Alert = "Dumping Public IDs is disallowed."
|
|
|
|
return connect.NewResponse(res), nil
|
|
}
|
|
|
|
api.executor.MapActionIdToBindingLock.RLock()
|
|
|
|
for k, v := range api.executor.MapActionIdToBinding {
|
|
res.Contents[k] = &apiv1.ActionEntityPair{
|
|
ActionTitle: v.Action.Title,
|
|
EntityPrefix: v.EntityPrefix,
|
|
}
|
|
}
|
|
|
|
api.executor.MapActionIdToBindingLock.RUnlock()
|
|
|
|
res.Alert = "Dumping variables has been enabled in the configuration. Please set InsecureAllowDumpActionMap = false again after you don't need it anymore"
|
|
|
|
return connect.NewResponse(res), nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) GetReadyz(ctx ctx.Context, req *connect.Request[apiv1.GetReadyzRequest]) (*connect.Response[apiv1.GetReadyzResponse], error) {
|
|
res := &apiv1.GetReadyzResponse{
|
|
Status: "OK",
|
|
}
|
|
|
|
return connect.NewResponse(res), nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) EventStream(ctx ctx.Context, req *connect.Request[apiv1.EventStreamRequest], srv *connect.ServerStream[apiv1.EventStreamResponse]) error {
|
|
log.Debugf("EventStream: %v", req.Msg)
|
|
|
|
client := &connectedClients{
|
|
channel: make(chan *apiv1.EventStreamResponse, 10), // Buffered channel to hold Events
|
|
AuthenticatedUser: acl.UserFromContext(ctx, api.cfg),
|
|
}
|
|
|
|
log.Infof("EventStream: client connected: %v", client.AuthenticatedUser.Username)
|
|
|
|
api.connectedClients = append(api.connectedClients, client)
|
|
|
|
// loop over client channel and send events to connectedClient
|
|
for msg := range client.channel {
|
|
log.Debugf("Sending event to client: %v", msg)
|
|
if err := srv.Send(msg); err != nil {
|
|
log.Errorf("Error sending event to client: %v", err)
|
|
}
|
|
}
|
|
|
|
log.Infof("EventStream: client disconnected")
|
|
|
|
return nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) OnActionMapRebuilt() {
|
|
for _, client := range api.connectedClients {
|
|
select {
|
|
case client.channel <- &apiv1.EventStreamResponse{
|
|
Event: &apiv1.EventStreamResponse_ConfigChanged{
|
|
ConfigChanged: &apiv1.EventConfigChanged{},
|
|
},
|
|
}:
|
|
default:
|
|
log.Warnf("EventStream: client channel is full, dropping message")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (api *oliveTinAPI) OnExecutionStarted(ex *executor.InternalLogEntry) {
|
|
for _, client := range api.connectedClients {
|
|
select {
|
|
case client.channel <- &apiv1.EventStreamResponse{
|
|
Event: &apiv1.EventStreamResponse_ExecutionStarted{
|
|
ExecutionStarted: &apiv1.EventExecutionStarted{
|
|
LogEntry: api.internalLogEntryToPb(ex, client.AuthenticatedUser),
|
|
},
|
|
},
|
|
}:
|
|
default:
|
|
log.Warnf("EventStream: client channel is full, dropping message")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (api *oliveTinAPI) OnExecutionFinished(ex *executor.InternalLogEntry) {
|
|
for _, client := range api.connectedClients {
|
|
select {
|
|
case client.channel <- &apiv1.EventStreamResponse{
|
|
Event: &apiv1.EventStreamResponse_ExecutionFinished{
|
|
ExecutionFinished: &apiv1.EventExecutionFinished{
|
|
LogEntry: api.internalLogEntryToPb(ex, client.AuthenticatedUser),
|
|
},
|
|
},
|
|
}:
|
|
default:
|
|
log.Warnf("EventStream: client channel is full, dropping message")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (api *oliveTinAPI) GetDiagnostics(ctx ctx.Context, req *connect.Request[apiv1.GetDiagnosticsRequest]) (*connect.Response[apiv1.GetDiagnosticsResponse], error) {
|
|
res := &apiv1.GetDiagnosticsResponse{
|
|
SshFoundKey: installationinfo.Runtime.SshFoundKey,
|
|
SshFoundConfig: installationinfo.Runtime.SshFoundConfig,
|
|
}
|
|
|
|
return connect.NewResponse(res), nil
|
|
}
|
|
|
|
func (api *oliveTinAPI) OnOutputChunk(content []byte, executionTrackingId string) {
|
|
for _, client := range api.connectedClients {
|
|
select {
|
|
case client.channel <- &apiv1.EventStreamResponse{
|
|
Event: &apiv1.EventStreamResponse_OutputChunk{
|
|
OutputChunk: &apiv1.EventOutputChunk{
|
|
Output: string(content),
|
|
ExecutionTrackingId: executionTrackingId,
|
|
},
|
|
},
|
|
}:
|
|
default:
|
|
log.Warnf("EventStream: client channel is full, dropping message")
|
|
}
|
|
}
|
|
}
|
|
|
|
func newServer(ex *executor.Executor) *oliveTinAPI {
|
|
server := oliveTinAPI{}
|
|
server.cfg = ex.Cfg
|
|
server.executor = ex
|
|
|
|
ex.AddListener(&server)
|
|
return &server
|
|
}
|
|
|
|
func GetNewHandler(ex *executor.Executor) (string, http.Handler) {
|
|
server := newServer(ex)
|
|
|
|
return apiv1connect.NewOliveTinApiServiceHandler(server)
|
|
}
|