Skip to content

Commit 5b3f032

Browse files
author
root
committed
fix appdetail
1 parent 8bbc135 commit 5b3f032

96 files changed

Lines changed: 4193 additions & 2872 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

backend/docs/openapi/api.yaml

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -965,9 +965,11 @@ paths:
965965
- Actions
966966
/api/actions/{id}/cancel:
967967
post:
968+
description: Immediately terminalizes one queued lifecycle action as cancelled before execution starts. Authenticated users only.
968969
operationId: post_api_actions_id_cancel
969970
parameters:
970-
- in: path
971+
- description: action id
972+
in: path
971973
name: id
972974
required: true
973975
schema:
@@ -983,17 +985,82 @@ paths:
983985
content:
984986
application/json:
985987
schema:
986-
$ref: '#/components/schemas/SuccessEnvelope'
988+
additionalProperties: true
989+
type: object
990+
description: OK
991+
"401":
992+
content:
993+
application/json:
994+
schema:
995+
$ref: '#/components/schemas/ErrorEnvelope'
996+
description: Unauthorized
997+
"404":
998+
content:
999+
application/json:
1000+
schema:
1001+
additionalProperties: true
1002+
type: object
1003+
description: Not Found
1004+
"409":
1005+
content:
1006+
application/json:
1007+
schema:
1008+
additionalProperties: true
1009+
type: object
1010+
description: Conflict
1011+
security:
1012+
- bearerAuth: []
1013+
summary: Cancel queued action
1014+
tags:
1015+
- Actions
1016+
/api/actions/{id}/force-fail:
1017+
post:
1018+
description: Immediately terminalizes one executing lifecycle action as failed and releases its active slot without guaranteeing rollback. Authenticated users only.
1019+
operationId: post_api_actions_id_force-fail
1020+
parameters:
1021+
- description: action id
1022+
in: path
1023+
name: id
1024+
required: true
1025+
schema:
1026+
type: string
1027+
requestBody:
1028+
content:
1029+
application/json:
1030+
schema:
1031+
$ref: '#/components/schemas/GenericRequest'
1032+
required: false
1033+
responses:
1034+
"200":
1035+
content:
1036+
application/json:
1037+
schema:
1038+
additionalProperties: true
1039+
type: object
9871040
description: OK
9881041
"401":
9891042
content:
9901043
application/json:
9911044
schema:
9921045
$ref: '#/components/schemas/ErrorEnvelope'
9931046
description: Unauthorized
1047+
"404":
1048+
content:
1049+
application/json:
1050+
schema:
1051+
additionalProperties: true
1052+
type: object
1053+
description: Not Found
1054+
"409":
1055+
content:
1056+
application/json:
1057+
schema:
1058+
additionalProperties: true
1059+
type: object
1060+
description: Conflict
9941061
security:
9951062
- bearerAuth: []
996-
summary: Create or execute actions by id cancel
1063+
summary: Force fail running action
9971064
tags:
9981065
- Actions
9991066
/api/actions/{id}/logs:

backend/docs/openapi/ext-api.yaml

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,11 +1047,13 @@ paths:
10471047
/api/actions/{id}/cancel:
10481048
post:
10491049
tags: [Actions]
1050-
summary: Create or execute actions by id cancel
1050+
summary: Cancel queued action
1051+
description: "Immediately terminalizes one queued lifecycle action as cancelled before execution starts. Authenticated users only."
10511052
operationId: post_api_actions_id_cancel
10521053
parameters:
10531054
- name: id
10541055
in: path
1056+
description: "action id"
10551057
required: true
10561058
schema:
10571059
type: string
@@ -1062,20 +1064,84 @@ paths:
10621064
schema:
10631065
$ref: '#/components/schemas/GenericRequest'
10641066
security:
1065-
- bearerAuth: [] # superuser required
1067+
- bearerAuth: []
10661068
responses:
10671069
"200":
10681070
description: OK
10691071
content:
10701072
application/json:
10711073
schema:
1072-
$ref: '#/components/schemas/SuccessEnvelope'
1074+
type: object
1075+
additionalProperties: true
10731076
"401":
10741077
description: Unauthorized
10751078
content:
10761079
application/json:
10771080
schema:
10781081
$ref: '#/components/schemas/ErrorEnvelope'
1082+
"404":
1083+
description: Not Found
1084+
content:
1085+
application/json:
1086+
schema:
1087+
type: object
1088+
additionalProperties: true
1089+
"409":
1090+
description: Conflict
1091+
content:
1092+
application/json:
1093+
schema:
1094+
type: object
1095+
additionalProperties: true
1096+
/api/actions/{id}/force-fail:
1097+
post:
1098+
tags: [Actions]
1099+
summary: Force fail running action
1100+
description: "Immediately terminalizes one executing lifecycle action as failed and releases its active slot without guaranteeing rollback. Authenticated users only."
1101+
operationId: post_api_actions_id_force-fail
1102+
parameters:
1103+
- name: id
1104+
in: path
1105+
description: "action id"
1106+
required: true
1107+
schema:
1108+
type: string
1109+
requestBody:
1110+
required: false
1111+
content:
1112+
application/json:
1113+
schema:
1114+
$ref: '#/components/schemas/GenericRequest'
1115+
security:
1116+
- bearerAuth: []
1117+
responses:
1118+
"200":
1119+
description: OK
1120+
content:
1121+
application/json:
1122+
schema:
1123+
type: object
1124+
additionalProperties: true
1125+
"401":
1126+
description: Unauthorized
1127+
content:
1128+
application/json:
1129+
schema:
1130+
$ref: '#/components/schemas/ErrorEnvelope'
1131+
"404":
1132+
description: Not Found
1133+
content:
1134+
application/json:
1135+
schema:
1136+
type: object
1137+
additionalProperties: true
1138+
"409":
1139+
description: Conflict
1140+
content:
1141+
application/json:
1142+
schema:
1143+
type: object
1144+
additionalProperties: true
10791145
/api/actions/{id}/logs:
10801146
get:
10811147
tags: [Actions]

backend/docs/openapi/group-matrix.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ groups:
8787
- GET /api/actions/{id}
8888
- DELETE /api/actions/{id}
8989
- POST /api/actions/{id}/cancel
90+
- POST /api/actions/{id}/force-fail
9091
- GET /api/actions/{id}/logs
9192
- GET /api/actions/{id}/stream
9293
- POST /api/actions/install/git-compose

backend/domain/config/sysconfig/schema/schema.go

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,33 @@ var entryCatalog = []EntrySchema{
192192
Module: "deploy",
193193
Key: "preflight",
194194
Fields: []FieldSchema{
195-
{ID: "minFreeDiskBytes", Label: "Min Free Disk Bytes", Type: "integer", HelpText: "Block installation when available disk falls below this threshold."},
195+
{ID: "minFreeDiskGiB", Label: "Minimum Free Disk (GiB)", Type: "number", HelpText: "Free disk floor before deploy."},
196+
},
197+
},
198+
{
199+
ID: "deploy-runtime",
200+
Title: "Deploy Runtime",
201+
Section: SectionWorkspace,
202+
Source: SourceCustom,
203+
Module: "deploy",
204+
Key: "runtime",
205+
Fields: []FieldSchema{
206+
{ID: "imagePullTimeoutSeconds", Label: "Image Pull Timeout Seconds", Type: "integer", HelpText: "Wait time for one image pull."},
207+
{ID: "composeUpTimeoutSeconds", Label: "Compose Up Timeout Seconds", Type: "integer", HelpText: "Wait time for docker compose up."},
208+
{ID: "healthCheckTimeoutSeconds", Label: "Health Check Timeout Seconds", Type: "integer", HelpText: "Wait time for health checks."},
209+
{ID: "runtimePullIdleHeartbeatSeconds", Label: "Runtime Pull Idle Heartbeat Seconds", Type: "integer", HelpText: "Idle time before pull heartbeat logs."},
210+
},
211+
},
212+
{
213+
ID: "deploy-git-defaults",
214+
Title: "Deploy Git Defaults",
215+
Section: SectionWorkspace,
216+
Source: SourceCustom,
217+
Module: "deploy",
218+
Key: "git-defaults",
219+
Fields: []FieldSchema{
220+
{ID: "defaultRef", Label: "Default Ref", Type: "string", HelpText: "Fallback Git ref."},
221+
{ID: "defaultComposePath", Label: "Default Compose Path", Type: "string", HelpText: "Fallback compose path."},
196222
},
197223
},
198224
{
@@ -412,7 +438,17 @@ var customSettingDefaults = map[string]map[string]any{
412438
"defaultAccessMode": "use_only",
413439
"clipboardClearSeconds": 0,
414440
},
415-
"deploy/preflight": {"minFreeDiskBytes": 512 * 1024 * 1024},
441+
"deploy/preflight": {"minFreeDiskGiB": 1.0},
442+
"deploy/runtime": {
443+
"imagePullTimeoutSeconds": 180,
444+
"composeUpTimeoutSeconds": 600,
445+
"healthCheckTimeoutSeconds": 120,
446+
"runtimePullIdleHeartbeatSeconds": 20,
447+
},
448+
"deploy/git-defaults": {
449+
"defaultRef": "main",
450+
"defaultComposePath": "docker-compose.yml",
451+
},
416452
"topic/share": {
417453
"shareMaxMinutes": 60,
418454
"shareDefaultMinutes": 30,

backend/domain/lifecycle/runtime/deployment_targets.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ type Executor interface {
3333
Name() string
3434
}
3535

36-
type localExecutor struct{}
36+
type localExecutor struct {
37+
app core.App
38+
}
3739

3840
func (e localExecutor) PrepareWorkspace(projectDir string, compose string) error {
3941
if err := os.MkdirAll(projectDir, 0o755); err != nil {
@@ -134,7 +136,7 @@ func executorName(serverID string) string {
134136

135137
func NewDeploymentExecutor(app core.App, serverID string) Executor {
136138
if executorName(serverID) == "local" {
137-
return localExecutor{}
139+
return localExecutor{app: app}
138140
}
139141
return newSSHExecutor(app, serverID)
140142
}

backend/domain/lifecycle/runtime/node_executor.go

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"time"
1313

1414
"github.com/pocketbase/pocketbase/core"
15+
"github.com/websoft9/appos/backend/domain/config/sysconfig"
16+
settingsschema "github.com/websoft9/appos/backend/domain/config/sysconfig/schema"
1517
"github.com/websoft9/appos/backend/domain/deploy"
1618
"github.com/websoft9/appos/backend/domain/lifecycle/model"
1719
"github.com/websoft9/appos/backend/infra/docker"
@@ -22,6 +24,39 @@ var sourceWorkspaceBasePath = "/appos/data"
2224
var sourceWorkspaceAllowedRoots = []string{"apps", "templates", "workflows"}
2325
var runtimePullIdleHeartbeatInterval = 20 * time.Second
2426

27+
func runtimeExecutorApp(executor Executor) core.App {
28+
switch typed := executor.(type) {
29+
case localExecutor:
30+
return typed.app
31+
case *localExecutor:
32+
return typed.app
33+
case sshExecutor:
34+
return typed.app
35+
case *sshExecutor:
36+
return typed.app
37+
default:
38+
return nil
39+
}
40+
}
41+
42+
func loadRuntimePullIdleHeartbeatInterval(app core.App) time.Duration {
43+
group, _ := sysconfig.GetGroup(app, "deploy", "runtime", settingsschema.DefaultGroup("deploy", "runtime"))
44+
seconds := sysconfig.Int(group, "runtimePullIdleHeartbeatSeconds", int((20 * time.Second) / time.Second))
45+
if seconds < 1 {
46+
seconds = 1
47+
}
48+
return time.Duration(seconds) * time.Second
49+
}
50+
51+
func loadRuntimeHealthCheckTimeout(app core.App) time.Duration {
52+
group, _ := sysconfig.GetGroup(app, "deploy", "runtime", settingsschema.DefaultGroup("deploy", "runtime"))
53+
seconds := sysconfig.Int(group, "healthCheckTimeoutSeconds", int((2 * time.Minute) / time.Second))
54+
if seconds < 1 {
55+
seconds = 1
56+
}
57+
return time.Duration(seconds) * time.Second
58+
}
59+
2560
func SetSourceWorkspaceBasePathForTest(basePath string) func() {
2661
previous := sourceWorkspaceBasePath
2762
sourceWorkspaceBasePath = basePath
@@ -295,7 +330,11 @@ func ExecuteNode(
295330
errCh <- scanner.Err()
296331
}()
297332

298-
ticker := time.NewTicker(runtimePullIdleHeartbeatInterval)
333+
heartbeatInterval := runtimePullIdleHeartbeatInterval
334+
if app := runtimeExecutorApp(executor); app != nil {
335+
heartbeatInterval = loadRuntimePullIdleHeartbeatInterval(app)
336+
}
337+
ticker := time.NewTicker(heartbeatInterval)
299338
defer ticker.Stop()
300339
hasOutput := false
301340
lastLoggedLine := ""
@@ -322,7 +361,7 @@ func ExecuteNode(
322361
logf("docker runtime pull: " + line)
323362
case <-ticker.C:
324363
rawIdleFor := time.Since(lastActivityAt)
325-
if rawIdleFor < runtimePullIdleHeartbeatInterval {
364+
if rawIdleFor < heartbeatInterval {
326365
continue
327366
}
328367
idleFor := formatRuntimePullIdleDuration(rawIdleFor)
@@ -416,7 +455,11 @@ func ExecuteNode(
416455
return result, err
417456
}
418457
result.DockerClient = client
419-
healthCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
458+
healthTimeout := 2 * time.Minute
459+
if app := runtimeExecutorApp(executor); app != nil {
460+
healthTimeout = loadRuntimeHealthCheckTimeout(app)
461+
}
462+
healthCtx, cancel := context.WithTimeout(ctx, healthTimeout)
420463
defer cancel()
421464
if err := healthCheck(healthCtx, client, operation.GetString("project_dir")); err != nil {
422465
return result, err

0 commit comments

Comments
 (0)