fix
This commit is contained in:
parent
1c9a192a96
commit
f3c4e3c60b
12 changed files with 95 additions and 16 deletions
45
cmd/telesrv-admin/buildinfo.go
Normal file
45
cmd/telesrv-admin/buildinfo.go
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "runtime/debug"
|
||||||
|
|
||||||
|
// gitCommit/buildTime can be set via -ldflags "-X main.gitCommit=... -X
|
||||||
|
// main.buildTime=...", mirroring cmd/telesrv/buildinfo.go -- but in
|
||||||
|
// practice neither procctl's goBuild (used by the admin panel's own
|
||||||
|
// Restart/Update) nor a plain `go build` sets them, so this normally falls
|
||||||
|
// back to Go's automatic VCS stamping (debug.ReadBuildInfo's vcs.revision),
|
||||||
|
// which needs nothing extra to work from a git checkout.
|
||||||
|
var (
|
||||||
|
gitCommit = ""
|
||||||
|
buildTime = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
type buildMetadata struct {
|
||||||
|
Commit string
|
||||||
|
Dirty bool
|
||||||
|
BuildTime string
|
||||||
|
}
|
||||||
|
|
||||||
|
// shortCommit is what the sidebar footer shows next to "Version: O7" -- the
|
||||||
|
// full hash is one click away in git log, the footer just needs enough to
|
||||||
|
// tell two builds apart at a glance.
|
||||||
|
func (m buildMetadata) shortCommit() string {
|
||||||
|
if len(m.Commit) > 7 {
|
||||||
|
return m.Commit[:7]
|
||||||
|
}
|
||||||
|
return m.Commit
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentBuildMetadata() buildMetadata {
|
||||||
|
meta := buildMetadata{Commit: gitCommit, BuildTime: buildTime}
|
||||||
|
if info, ok := debug.ReadBuildInfo(); ok {
|
||||||
|
settings := map[string]string{}
|
||||||
|
for _, setting := range info.Settings {
|
||||||
|
settings[setting.Key] = setting.Value
|
||||||
|
}
|
||||||
|
if meta.Commit == "" {
|
||||||
|
meta.Commit = settings["vcs.revision"]
|
||||||
|
}
|
||||||
|
meta.Dirty = settings["vcs.modified"] == "true"
|
||||||
|
}
|
||||||
|
return meta
|
||||||
|
}
|
||||||
|
|
@ -292,6 +292,7 @@ func (s *server) handleAPILogout(w http.ResponseWriter, _ *http.Request) {
|
||||||
// session carries, so the UI can hide a section the operator may not use rather
|
// session carries, so the UI can hide a section the operator may not use rather
|
||||||
// than letting them walk into a 403.
|
// than letting them walk into a 403.
|
||||||
func (s *server) handleSession(w http.ResponseWriter, r *http.Request) {
|
func (s *server) handleSession(w http.ResponseWriter, r *http.Request) {
|
||||||
|
build := currentBuildMetadata()
|
||||||
writeJSON(w, http.StatusOK, map[string]any{
|
writeJSON(w, http.StatusOK, map[string]any{
|
||||||
"actor": actorFromContext(r.Context()),
|
"actor": actorFromContext(r.Context()),
|
||||||
"permissions": permissionsFromContext(r.Context()).List(),
|
"permissions": permissionsFromContext(r.Context()).List(),
|
||||||
|
|
@ -302,6 +303,15 @@ func (s *server) handleSession(w http.ResponseWriter, r *http.Request) {
|
||||||
// tells "the old admin process died and a new one answered" apart
|
// tells "the old admin process died and a new one answered" apart
|
||||||
// from "the old one is just slow to respond".
|
// from "the old one is just slow to respond".
|
||||||
"boot_id": bootID,
|
"boot_id": bootID,
|
||||||
|
// build is this admin binary's own commit -- shown under "Version"
|
||||||
|
// in the sidebar footer so an operator can tell at a glance which
|
||||||
|
// build is actually running, independent of the app version string.
|
||||||
|
"build": map[string]any{
|
||||||
|
"commit": build.Commit,
|
||||||
|
"short_commit": build.shortCommit(),
|
||||||
|
"dirty": build.Dirty,
|
||||||
|
"build_time": build.BuildTime,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -262,7 +262,7 @@ func (s *server) handleRestartServerAPI(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
meta := s.commandMetaFromAPI(r, body.CommandID, body.Reason, body.Confirm, "restart-server")
|
meta := s.commandMetaFromAPI(r, body.CommandID, body.Reason, body.Confirm, "restart-server")
|
||||||
if meta.DryRun {
|
if meta.DryRun {
|
||||||
writeJSON(w, http.StatusOK, serverCommandResult(meta, "server.restart", nil, "restart validated -- rebuilds and relaunches bin/owpengram-server", nil))
|
writeJSON(w, http.StatusOK, serverCommandResult(meta, "server.restart", nil, "restart validated -- rebuilds both bin/owpengram-server and bin/owpengram-admin-panel, relaunches owpengram-server", nil))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log, err := s.serverCtl.Restart(r.Context())
|
log, err := s.serverCtl.Restart(r.Context())
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
cmd/telesrv-admin/web/dist/assets/index-hA2EpjuH.css
vendored
Normal file
1
cmd/telesrv-admin/web/dist/assets/index-hA2EpjuH.css
vendored
Normal file
File diff suppressed because one or more lines are too long
4
cmd/telesrv-admin/web/dist/index.html
vendored
4
cmd/telesrv-admin/web/dist/index.html
vendored
|
|
@ -23,8 +23,8 @@
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="module" crossorigin src="/assets/index-qfdwgPZE.js"></script>
|
<script type="module" crossorigin src="/assets/index-CgFYD-Jw.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-Dc3900m_.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-hA2EpjuH.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ export function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PermissionsProvider permissions={session.permissions ?? []} hideThirdPartyVerification={session.hide_third_party_verification ?? true}>
|
<PermissionsProvider permissions={session.permissions ?? []} hideThirdPartyVerification={session.hide_third_party_verification ?? true}>
|
||||||
<Shell actor={session.actor} route={route} navigate={navigate} onLogout={() => setSession(null)}>
|
<Shell actor={session.actor} build={session.build} route={route} navigate={navigate} onLogout={() => setSession(null)}>
|
||||||
<Routes route={route} navigate={navigate} />
|
<Routes route={route} navigate={navigate} />
|
||||||
</Shell>
|
</Shell>
|
||||||
</PermissionsProvider>
|
</PermissionsProvider>
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,14 @@ export function BootScreen() {
|
||||||
|
|
||||||
export function Shell({
|
export function Shell({
|
||||||
actor,
|
actor,
|
||||||
|
build,
|
||||||
route,
|
route,
|
||||||
navigate,
|
navigate,
|
||||||
onLogout,
|
onLogout,
|
||||||
children
|
children
|
||||||
}: {
|
}: {
|
||||||
actor: string;
|
actor: string;
|
||||||
|
build?: { commit: string; short_commit: string; dirty: boolean; build_time: string };
|
||||||
route: RouteState;
|
route: RouteState;
|
||||||
navigate: Navigate;
|
navigate: Navigate;
|
||||||
onLogout: () => void;
|
onLogout: () => void;
|
||||||
|
|
@ -143,6 +145,11 @@ export function Shell({
|
||||||
</nav>
|
</nav>
|
||||||
<div className="sidebar-status">
|
<div className="sidebar-status">
|
||||||
<span className="sidebar-label">{"Version: O7"}</span>
|
<span className="sidebar-label">{"Version: O7"}</span>
|
||||||
|
{build?.short_commit && (
|
||||||
|
<span className="sidebar-label sidebar-build" title={build.commit + (build.dirty ? " (uncommitted changes)" : "")}>
|
||||||
|
{`Build: ${build.short_commit}${build.dirty ? "+" : ""}`}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
<div className="workspace">
|
<div className="workspace">
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,13 @@ a {
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-build {
|
||||||
|
font-weight: 500;
|
||||||
|
text-transform: none;
|
||||||
|
font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-list {
|
.nav-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
|
|
||||||
|
|
@ -584,6 +584,14 @@ export type AdminSession = {
|
||||||
// comment. Used by Server Settings' Restart/Update flow to detect a
|
// comment. Used by Server Settings' Restart/Update flow to detect a
|
||||||
// genuinely new admin process after asking it to bounce.
|
// genuinely new admin process after asking it to bounce.
|
||||||
boot_id?: string;
|
boot_id?: string;
|
||||||
|
// This admin binary's own build -- shown under "Version" in the sidebar
|
||||||
|
// footer so an operator can tell which build is actually running.
|
||||||
|
build?: {
|
||||||
|
commit: string;
|
||||||
|
short_commit: string;
|
||||||
|
dirty: boolean;
|
||||||
|
build_time: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AdminLoginResult = AdminSession & {
|
export type AdminLoginResult = AdminSession & {
|
||||||
|
|
|
||||||
|
|
@ -387,9 +387,13 @@ func (m *Manager) goBuild(ctx context.Context, outPath, pkg string) (string, err
|
||||||
|
|
||||||
// --- high-level actions ----------------------------------------------------
|
// --- high-level actions ----------------------------------------------------
|
||||||
|
|
||||||
// Restart rebuilds and relaunches only bin/owpengram-server (the MTProto
|
// Restart rebuilds BOTH bin/owpengram-server and bin/owpengram-admin-panel
|
||||||
// data-plane process) -- never the admin binary currently handling this
|
// from the current working tree (no git pull -- see Update for that) and
|
||||||
// request. Returns a combined build/relaunch log for the admin UI.
|
// relaunches owpengram-server, which then bounces the admin panel onto its
|
||||||
|
// freshly built binary. Never self-restarts the admin process handling this
|
||||||
|
// request directly -- see HandlePendingAdminRestart's doc comment for why
|
||||||
|
// that handoff happens from the newly launched server instead. Returns a
|
||||||
|
// combined build/relaunch log for the admin UI.
|
||||||
func (m *Manager) Restart(ctx context.Context) (string, error) {
|
func (m *Manager) Restart(ctx context.Context) (string, error) {
|
||||||
st := m.loadState()
|
st := m.loadState()
|
||||||
if pidAlive(st.ServerPID) {
|
if pidAlive(st.ServerPID) {
|
||||||
|
|
@ -399,7 +403,7 @@ func (m *Manager) Restart(ctx context.Context) (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dockerLog, err
|
return dockerLog, err
|
||||||
}
|
}
|
||||||
buildLog, err := m.buildServer(ctx)
|
buildLog, err := m.buildBoth(ctx)
|
||||||
fullLog := dockerLog + "\n" + buildLog
|
fullLog := dockerLog + "\n" + buildLog
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fullLog, fmt.Errorf("build failed: %w", err)
|
return fullLog, fmt.Errorf("build failed: %w", err)
|
||||||
|
|
@ -416,13 +420,11 @@ func (m *Manager) Restart(ctx context.Context) (string, error) {
|
||||||
if err := m.saveState(st); err != nil {
|
if err := m.saveState(st); err != nil {
|
||||||
return fullLog, fmt.Errorf("save state: %w", err)
|
return fullLog, fmt.Errorf("save state: %w", err)
|
||||||
}
|
}
|
||||||
return fullLog + fmt.Sprintf("\nowpengram-server relaunched, pid=%d. Admin panel will restart shortly.\n", pid), nil
|
return fullLog + fmt.Sprintf("\nowpengram-server relaunched, pid=%d. Admin panel will restart shortly onto its freshly built binary.\n", pid), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update runs git pull, ensures Docker infrastructure is up, rebuilds both
|
// Update is Restart plus a `git pull --ff-only` first, so a fresh checkout
|
||||||
// binaries, then does the same server-only relaunch as Restart -- including
|
// gets built instead of whatever's already on disk.
|
||||||
// asking that new process to bounce the admin panel too, now onto its
|
|
||||||
// freshly built binary.
|
|
||||||
func (m *Manager) Update(ctx context.Context) (string, error) {
|
func (m *Manager) Update(ctx context.Context) (string, error) {
|
||||||
pullLog, err := m.GitPull(ctx)
|
pullLog, err := m.GitPull(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue