added messages templates
This commit is contained in:
parent
e8dc967e6a
commit
7cd1f64d0d
29 changed files with 1266 additions and 84 deletions
|
|
@ -116,6 +116,21 @@ type uiConfig struct {
|
|||
// directory owpengram-server reads, so an identity edit here is visible
|
||||
// over /owpengram/server-info immediately (see internal/identity).
|
||||
IdentityDir string
|
||||
// WelcomeMessagePhoneDefault/WelcomeMessageEmailDefault mirror
|
||||
// config.WelcomeMessage{Phone,Email}Template -- the env-var-resolved
|
||||
// fallback text (TELESRV_WELCOME_MESSAGE_*_TEMPLATE, itself defaulting
|
||||
// to the compiled-in copy) the running owpengram-server process falls
|
||||
// back to whenever the identity panel override is unset. Surfaced as
|
||||
// "the effective default" in the Server Settings login-notifications
|
||||
// panel, assuming both binaries share the same .env.
|
||||
WelcomeMessagePhoneDefault string
|
||||
WelcomeMessageEmailDefault string
|
||||
// LoginCodeMessageDefault mirrors config.LoginCodeMessageTemplate -- the
|
||||
// env-var-resolved fallback text (TELESRV_LOGIN_CODE_MESSAGE_TEMPLATE,
|
||||
// itself defaulting to the compiled-in copy) the running owpengram-server
|
||||
// process falls back to whenever the identity panel override is unset.
|
||||
// Same "effective default" contract as WelcomeMessage{Phone,Email}Default.
|
||||
LoginCodeMessageDefault string
|
||||
// RepoRoot is where Server Settings' Restart/Update/.env-editing (see
|
||||
// internal/procctl) operate: bin/, logs/, .env, .env.example and
|
||||
// .server_panel.json are all expected directly under it, exactly as
|
||||
|
|
@ -167,6 +182,9 @@ func loadConfig() (uiConfig, error) {
|
|||
Permissions: appCfg.AdminUIPermissions,
|
||||
HideThirdPartyVerification: appCfg.HideThirdPartyVerification,
|
||||
IdentityDir: appCfg.IdentityDir,
|
||||
WelcomeMessagePhoneDefault: appCfg.WelcomeMessagePhoneTemplate,
|
||||
WelcomeMessageEmailDefault: appCfg.WelcomeMessageEmailTemplate,
|
||||
LoginCodeMessageDefault: appCfg.LoginCodeMessageTemplate,
|
||||
RepoRoot: repoRoot,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,6 +178,8 @@ func (s *server) routes() http.Handler {
|
|||
mux.Handle("GET /api/server/identity", s.serverManage(s.handleServerIdentityAPI))
|
||||
mux.Handle("GET /api/server/icon", s.serverManage(s.handleServerIconAPI))
|
||||
mux.Handle("POST /api/actions/set-server-identity", s.serverManage(s.handleSetServerIdentityAPI))
|
||||
mux.Handle("POST /api/actions/set-welcome-message-templates", s.serverManage(s.handleSetWelcomeMessageTemplatesAPI))
|
||||
mux.Handle("POST /api/actions/set-login-code-message-template", s.serverManage(s.handleSetLoginCodeMessageTemplateAPI))
|
||||
mux.Handle("POST /api/actions/upload-server-icon", s.serverManage(s.handleUploadServerIconAPI))
|
||||
mux.Handle("POST /api/actions/remove-server-icon", s.serverManage(s.handleRemoveServerIconAPI))
|
||||
mux.Handle("GET /api/server/env", s.serverManage(s.handleServerEnvAPI))
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import (
|
|||
"strings"
|
||||
|
||||
"telesrv/internal/admin"
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/identity"
|
||||
)
|
||||
|
||||
// serverManage gates the whole Server Settings surface -- see
|
||||
|
|
@ -47,13 +49,39 @@ func serverCommandResult(meta admin.CommandMeta, action string, err error, messa
|
|||
|
||||
// --- identity (name/description/icon) ---------------------------------
|
||||
|
||||
// serverIdentityAPIResponse extends identity.Info's raw fields with the two
|
||||
// *effective* fallback welcome-message templates -- s.cfg's
|
||||
// WelcomeMessage{Phone,Email}Default, i.e. this admin process's own reading
|
||||
// of TELESRV_WELCOME_MESSAGE_*_TEMPLATE (env var, itself defaulting to the
|
||||
// compiled-in copy), which matches what owpengram-server falls back to
|
||||
// whenever the panel override is unset, as long as both processes share the
|
||||
// same .env (see uiConfig.WelcomeMessagePhoneDefault's doc comment). The
|
||||
// panel needs both: the raw override (possibly empty) to know whether a
|
||||
// field is "explicitly set", and the default text to show as "(using
|
||||
// default: ...)" / to restore on Reset.
|
||||
type serverIdentityAPIResponse struct {
|
||||
identity.Info
|
||||
DefaultWelcomeMessagePhoneTemplate string `json:"default_welcome_message_phone_template"`
|
||||
DefaultWelcomeMessageEmailTemplate string `json:"default_welcome_message_email_template"`
|
||||
// DefaultLoginCodeMessageTemplate is the effective fallback text for
|
||||
// the login-code delivery message (s.cfg.LoginCodeMessageDefault) --
|
||||
// same "raw override + effective default" contract as the two fields
|
||||
// above, see their doc comment.
|
||||
DefaultLoginCodeMessageTemplate string `json:"default_login_code_message_template"`
|
||||
}
|
||||
|
||||
func (s *server) handleServerIdentityAPI(w http.ResponseWriter, r *http.Request) {
|
||||
info, err := s.identity.Get()
|
||||
if err != nil {
|
||||
writeAPIError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, info)
|
||||
writeJSON(w, http.StatusOK, serverIdentityAPIResponse{
|
||||
Info: info,
|
||||
DefaultWelcomeMessagePhoneTemplate: s.cfg.WelcomeMessagePhoneDefault,
|
||||
DefaultWelcomeMessageEmailTemplate: s.cfg.WelcomeMessageEmailDefault,
|
||||
DefaultLoginCodeMessageTemplate: s.cfg.LoginCodeMessageDefault,
|
||||
})
|
||||
}
|
||||
|
||||
// handleServerIconAPI serves the icon's raw bytes for the panel's own
|
||||
|
|
@ -101,6 +129,84 @@ func (s *server) handleSetServerIdentityAPI(w http.ResponseWriter, r *http.Reque
|
|||
writeJSON(w, http.StatusOK, serverCommandResult(meta, "server.set_identity", err, "server identity updated", details))
|
||||
}
|
||||
|
||||
// --- login-notification templates ---------------------------------------
|
||||
|
||||
type setWelcomeMessageTemplatesAPIRequest struct {
|
||||
CommandID string `json:"command_id"`
|
||||
Reason string `json:"reason"`
|
||||
Confirm bool `json:"confirm"`
|
||||
PhoneTemplate string `json:"phone_template"`
|
||||
EmailTemplate string `json:"email_template"`
|
||||
}
|
||||
|
||||
// handleSetWelcomeMessageTemplatesAPI sets (or, with an empty string,
|
||||
// clears) the admin-panel override for the 777000 login-notification
|
||||
// message's phone/email template -- see identity.Store.SetWelcomeMessageTemplates
|
||||
// and domain.ResolveWelcomeMessageTemplate. Deliberately a separate endpoint
|
||||
// from set-server-identity: brand identity (name/description/icon) and
|
||||
// login-notification copy are different concerns that happen to share the
|
||||
// same on-disk identity.json, and keeping them as separate actions/buttons
|
||||
// means editing one never risks silently blanking the other.
|
||||
func (s *server) handleSetWelcomeMessageTemplatesAPI(w http.ResponseWriter, r *http.Request) {
|
||||
var body setWelcomeMessageTemplatesAPIRequest
|
||||
if !decodeAction(w, r, &body) {
|
||||
return
|
||||
}
|
||||
meta := s.commandMetaFromAPI(r, body.CommandID, body.Reason, body.Confirm, "set-welcome-message-templates")
|
||||
details := map[string]any{
|
||||
"phone_template_set": strings.TrimSpace(body.PhoneTemplate) != "",
|
||||
"email_template_set": strings.TrimSpace(body.EmailTemplate) != "",
|
||||
}
|
||||
if meta.DryRun {
|
||||
writeJSON(w, http.StatusOK, serverCommandResult(meta, "server.set_welcome_message_templates", nil, "login-notification templates validated", details))
|
||||
return
|
||||
}
|
||||
err := s.identity.SetWelcomeMessageTemplates(body.PhoneTemplate, body.EmailTemplate)
|
||||
writeJSON(w, http.StatusOK, serverCommandResult(meta, "server.set_welcome_message_templates", err, "login-notification templates updated", details))
|
||||
}
|
||||
|
||||
// --- login-code delivery message template --------------------------------
|
||||
|
||||
type setLoginCodeMessageTemplateAPIRequest struct {
|
||||
CommandID string `json:"command_id"`
|
||||
Reason string `json:"reason"`
|
||||
Confirm bool `json:"confirm"`
|
||||
Template string `json:"template"`
|
||||
}
|
||||
|
||||
// handleSetLoginCodeMessageTemplateAPI sets (or, with an empty string,
|
||||
// clears) the admin-panel override for the 777000 login-code delivery
|
||||
// message -- see identity.Store.SetLoginCodeMessageTemplate and
|
||||
// domain.ResolveLoginCodeMessageTemplate. A dedicated endpoint (not folded
|
||||
// into set-welcome-message-templates): this message embeds the actual OTP
|
||||
// code via the {{code}} placeholder, so a save here carries an extra,
|
||||
// security-relevant validation the login-notification templates don't
|
||||
// need -- a template missing {{code}} (or containing it more than once)
|
||||
// would either silently drop the code from the message or leave it
|
||||
// ambiguous which occurrence carries it, so it is rejected outright with a
|
||||
// 422 rather than saved. Clearing the override (empty string) is exempt --
|
||||
// it always resolves to a valid built-in/env default.
|
||||
func (s *server) handleSetLoginCodeMessageTemplateAPI(w http.ResponseWriter, r *http.Request) {
|
||||
var body setLoginCodeMessageTemplateAPIRequest
|
||||
if !decodeAction(w, r, &body) {
|
||||
return
|
||||
}
|
||||
if t := strings.TrimSpace(body.Template); t != "" {
|
||||
if err := domain.ValidateLoginCodeMessageTemplate(t); err != nil {
|
||||
writeAPIError(w, http.StatusUnprocessableEntity, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
meta := s.commandMetaFromAPI(r, body.CommandID, body.Reason, body.Confirm, "set-login-code-message-template")
|
||||
details := map[string]any{"template_set": strings.TrimSpace(body.Template) != ""}
|
||||
if meta.DryRun {
|
||||
writeJSON(w, http.StatusOK, serverCommandResult(meta, "server.set_login_code_message_template", nil, "login-code message template validated", details))
|
||||
return
|
||||
}
|
||||
err := s.identity.SetLoginCodeMessageTemplate(body.Template)
|
||||
writeJSON(w, http.StatusOK, serverCommandResult(meta, "server.set_login_code_message_template", err, "login-code message template updated", details))
|
||||
}
|
||||
|
||||
var allowedServerIconExts = map[string]bool{
|
||||
".png": true, ".jpg": true, ".jpeg": true, ".webp": true, ".gif": true,
|
||||
}
|
||||
|
|
|
|||
87
cmd/telesrv-admin/serversettings_test.go
Normal file
87
cmd/telesrv-admin/serversettings_test.go
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"telesrv/internal/identity"
|
||||
)
|
||||
|
||||
func TestServerIdentityAPIReportsOverridesAndDefaultsSeparately(t *testing.T) {
|
||||
store := identity.NewStore(t.TempDir())
|
||||
srv := &server{
|
||||
cfg: uiConfig{
|
||||
WelcomeMessagePhoneDefault: "env phone default",
|
||||
WelcomeMessageEmailDefault: "env email default",
|
||||
},
|
||||
identity: store,
|
||||
}
|
||||
|
||||
// Before any override: GET must report empty raw fields (so the UI can
|
||||
// tell "unset" apart from "explicitly set to the same text as the
|
||||
// default"), alongside the effective default text.
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/server/identity", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
srv.handleServerIdentityAPI(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("GET status = %d, body = %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
var resp serverIdentityAPIResponse
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("decode: %v", err)
|
||||
}
|
||||
if resp.WelcomeMessagePhoneTemplate != "" || resp.WelcomeMessageEmailTemplate != "" {
|
||||
t.Fatalf("expected empty overrides before any Set, got %+v", resp)
|
||||
}
|
||||
if resp.DefaultWelcomeMessagePhoneTemplate != "env phone default" || resp.DefaultWelcomeMessageEmailTemplate != "env email default" {
|
||||
t.Fatalf("expected effective defaults from cfg, got %+v", resp)
|
||||
}
|
||||
|
||||
// Set an override for phone only.
|
||||
setReq := httptest.NewRequest(http.MethodPost, "/api/actions/set-welcome-message-templates", strings.NewReader(`{
|
||||
"reason": "test",
|
||||
"confirm": true,
|
||||
"phone_template": "custom phone template"
|
||||
}`))
|
||||
setRec := httptest.NewRecorder()
|
||||
srv.handleSetWelcomeMessageTemplatesAPI(setRec, setReq)
|
||||
if setRec.Code != http.StatusOK {
|
||||
t.Fatalf("SET status = %d, body = %s", setRec.Code, setRec.Body.String())
|
||||
}
|
||||
|
||||
req2 := httptest.NewRequest(http.MethodGet, "/api/server/identity", nil)
|
||||
rec2 := httptest.NewRecorder()
|
||||
srv.handleServerIdentityAPI(rec2, req2)
|
||||
var resp2 serverIdentityAPIResponse
|
||||
if err := json.Unmarshal(rec2.Body.Bytes(), &resp2); err != nil {
|
||||
t.Fatalf("decode: %v", err)
|
||||
}
|
||||
if resp2.WelcomeMessagePhoneTemplate != "custom phone template" {
|
||||
t.Fatalf("expected phone override to be set, got %+v", resp2)
|
||||
}
|
||||
if resp2.WelcomeMessageEmailTemplate != "" {
|
||||
t.Fatalf("expected email override to stay unset, got %+v", resp2)
|
||||
}
|
||||
|
||||
// Reset (empty string) clears the override back to "unset".
|
||||
resetReq := httptest.NewRequest(http.MethodPost, "/api/actions/set-welcome-message-templates", strings.NewReader(`{
|
||||
"reason": "test",
|
||||
"confirm": true,
|
||||
"phone_template": ""
|
||||
}`))
|
||||
resetRec := httptest.NewRecorder()
|
||||
srv.handleSetWelcomeMessageTemplatesAPI(resetRec, resetReq)
|
||||
if resetRec.Code != http.StatusOK {
|
||||
t.Fatalf("reset status = %d, body = %s", resetRec.Code, resetRec.Body.String())
|
||||
}
|
||||
info, err := store.Get()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if info.WelcomeMessagePhoneTemplate != "" {
|
||||
t.Fatalf("expected phone override cleared after reset, got %+v", info)
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
2
cmd/telesrv-admin/web/dist/index.html
vendored
2
cmd/telesrv-admin/web/dist/index.html
vendored
|
|
@ -23,7 +23,7 @@
|
|||
})();
|
||||
</script>
|
||||
|
||||
<script type="module" crossorigin src="/assets/index-TfcI68oK.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-BTi0-ijr.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-0MvM-hpw.css">
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ export function ServerSettingsPage() {
|
|||
{tab === "settings" ? (
|
||||
<div className="stacked-sections">
|
||||
<IdentitySection />
|
||||
<LoginNotificationsSection />
|
||||
<EnvSection />
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -127,6 +128,168 @@ function IdentitySection() {
|
|||
);
|
||||
}
|
||||
|
||||
// --- Login notifications ------------------------------------------------
|
||||
|
||||
// LoginNotificationsSection edits the 777000 login-notification message's
|
||||
// per-method (phone/email) template -- a different concern from brand
|
||||
// identity above even though both live in the same identity.json (see
|
||||
// cmd/telesrv-admin/serversettings.go's handleSetWelcomeMessageTemplatesAPI
|
||||
// doc comment), so it gets its own card and its own save action.
|
||||
function LoginNotificationsSection() {
|
||||
const [identity, setIdentity] = useState<ServerIdentity | null>(null);
|
||||
const [phoneTemplate, setPhoneTemplate] = useState("");
|
||||
const [emailTemplate, setEmailTemplate] = useState("");
|
||||
const [codeTemplate, setCodeTemplate] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
|
||||
async function load() {
|
||||
setError("");
|
||||
try {
|
||||
const info = await api.serverIdentity();
|
||||
setIdentity(info);
|
||||
setPhoneTemplate(info.welcome_message_phone_template ?? "");
|
||||
setEmailTemplate(info.welcome_message_email_template ?? "");
|
||||
setCodeTemplate(info.login_code_message_template ?? "");
|
||||
} catch (err) {
|
||||
setError(errorMessage(err));
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => { void load(); }, []);
|
||||
|
||||
const phoneIsOverridden = phoneTemplate.trim() !== "";
|
||||
const emailIsOverridden = emailTemplate.trim() !== "";
|
||||
const codeIsOverridden = codeTemplate.trim() !== "";
|
||||
// Mirrors the server-side check in handleSetLoginCodeMessageTemplateAPI --
|
||||
// disable the save button instead of letting the operator submit a
|
||||
// template that would silently never deliver the actual OTP code.
|
||||
const codeOccurrences = (codeTemplate.match(/\{\{code\}\}/g) ?? []).length;
|
||||
const codeTemplateInvalid = codeIsOverridden && codeOccurrences !== 1;
|
||||
|
||||
return (
|
||||
<section className="section-block">
|
||||
<SectionHead title={"Login notifications"} />
|
||||
{error && <Alert>{error}</Alert>}
|
||||
{!identity ? (
|
||||
<LoadingSurface label={"Loading login notification templates..."} />
|
||||
) : (
|
||||
<div className="card-body">
|
||||
<p style={{ color: "var(--muted)", marginTop: 0 }}>
|
||||
{"Sent from the official system account on every completed sign-in. Use "}
|
||||
<code>{"{{server_name}}"}</code>
|
||||
{" to insert the server's configured name."}
|
||||
</p>
|
||||
<label className="form-field">
|
||||
<span>
|
||||
{"Phone sign-in template"}
|
||||
{" "}
|
||||
{phoneIsOverridden ? <span className="badge good">{"custom"}</span> : <span className="badge">{"default"}</span>}
|
||||
</span>
|
||||
<textarea
|
||||
rows={4}
|
||||
value={phoneTemplate}
|
||||
onChange={(event) => setPhoneTemplate(event.target.value)}
|
||||
placeholder={identity.default_welcome_message_phone_template}
|
||||
/>
|
||||
</label>
|
||||
<div className="gift-table-actions">
|
||||
<ActionButton
|
||||
tone="neutral"
|
||||
compact
|
||||
label={"Reset to default"}
|
||||
path="/api/actions/set-welcome-message-templates"
|
||||
payload={() => ({ phone_template: "", email_template: emailTemplate })}
|
||||
disabled={!phoneIsOverridden}
|
||||
onDone={() => { setPhoneTemplate(""); void load(); }}
|
||||
/>
|
||||
</div>
|
||||
<label className="form-field">
|
||||
<span>
|
||||
{"Email sign-in template"}
|
||||
{" "}
|
||||
{emailIsOverridden ? <span className="badge good">{"custom"}</span> : <span className="badge">{"default"}</span>}
|
||||
</span>
|
||||
<textarea
|
||||
rows={4}
|
||||
value={emailTemplate}
|
||||
onChange={(event) => setEmailTemplate(event.target.value)}
|
||||
placeholder={identity.default_welcome_message_email_template}
|
||||
/>
|
||||
</label>
|
||||
<div className="gift-table-actions">
|
||||
<ActionButton
|
||||
tone="neutral"
|
||||
compact
|
||||
label={"Reset to default"}
|
||||
path="/api/actions/set-welcome-message-templates"
|
||||
payload={() => ({ phone_template: phoneTemplate, email_template: "" })}
|
||||
disabled={!emailIsOverridden}
|
||||
onDone={() => { setEmailTemplate(""); void load(); }}
|
||||
/>
|
||||
</div>
|
||||
<div className="gift-table-actions identity-save-row">
|
||||
<ActionButton
|
||||
tone="neutral"
|
||||
label={"Save login notification templates"}
|
||||
path="/api/actions/set-welcome-message-templates"
|
||||
payload={() => ({ phone_template: phoneTemplate, email_template: emailTemplate })}
|
||||
onDone={() => void load()}
|
||||
/>
|
||||
</div>
|
||||
<p style={{ color: "var(--muted)", marginTop: "1.5em", borderTop: "1px solid var(--line)", paddingTop: "1em" }}>
|
||||
{"Sent from the official system account with every login code (SMS and email alike). Must contain "}
|
||||
<code>{"{{code}}"}</code>
|
||||
{" exactly once -- that's where the actual code is inserted and bolded. "}
|
||||
<code>{"{{server_name}}"}</code>
|
||||
{" is optional and may appear any number of times."}
|
||||
</p>
|
||||
<label className="form-field">
|
||||
<span>
|
||||
{"Login-code message template"}
|
||||
{" "}
|
||||
{codeIsOverridden ? <span className="badge good">{"custom"}</span> : <span className="badge">{"default"}</span>}
|
||||
</span>
|
||||
<textarea
|
||||
rows={5}
|
||||
value={codeTemplate}
|
||||
onChange={(event) => setCodeTemplate(event.target.value)}
|
||||
placeholder={identity.default_login_code_message_template}
|
||||
/>
|
||||
{codeTemplateInvalid && (
|
||||
<span style={{ color: "var(--danger-text)", fontSize: "0.85em" }}>
|
||||
{codeOccurrences === 0
|
||||
? "Must contain {{code}} exactly once -- it is currently missing."
|
||||
: `Must contain {{code}} exactly once -- it currently appears ${codeOccurrences} times.`}
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
<div className="gift-table-actions">
|
||||
<ActionButton
|
||||
tone="neutral"
|
||||
compact
|
||||
label={"Reset to default"}
|
||||
path="/api/actions/set-login-code-message-template"
|
||||
payload={() => ({ template: "" })}
|
||||
disabled={!codeIsOverridden}
|
||||
onDone={() => { setCodeTemplate(""); void load(); }}
|
||||
/>
|
||||
</div>
|
||||
<div className="gift-table-actions identity-save-row">
|
||||
<ActionButton
|
||||
tone="neutral"
|
||||
label={"Save login-code message template"}
|
||||
path="/api/actions/set-login-code-message-template"
|
||||
payload={() => ({ template: codeTemplate })}
|
||||
disabled={codeTemplateInvalid}
|
||||
onDone={() => void load()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function ServerIconModal({ hasIcon, onClose, onDone }: { hasIcon: boolean; onClose: () => void; onDone: () => void }) {
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [previewURL, setPreviewURL] = useState("");
|
||||
|
|
|
|||
|
|
@ -851,6 +851,26 @@ export type ServerIdentity = {
|
|||
name: string;
|
||||
description: string;
|
||||
icon_ext?: string;
|
||||
// welcome_message_*_template: raw admin-panel override for the 777000
|
||||
// login-notification message, empty when unset (falls back to the
|
||||
// TELESRV_WELCOME_MESSAGE_*_TEMPLATE env var, then a built-in default).
|
||||
welcome_message_phone_template?: string;
|
||||
welcome_message_email_template?: string;
|
||||
// default_welcome_message_*_template: the effective fallback text this
|
||||
// admin process currently reads (env var if set, else the compiled-in
|
||||
// copy) -- shown when the override above is empty.
|
||||
default_welcome_message_phone_template: string;
|
||||
default_welcome_message_email_template: string;
|
||||
// login_code_message_template: raw admin-panel override for the 777000
|
||||
// login-code delivery message, empty when unset (falls back to the
|
||||
// TELESRV_LOGIN_CODE_MESSAGE_TEMPLATE env var, then a built-in default).
|
||||
// Unlike the welcome_message_* templates there is only one -- the
|
||||
// message never varies by delivery channel. Must contain the {{code}}
|
||||
// placeholder exactly once (enforced server-side on save).
|
||||
login_code_message_template?: string;
|
||||
// default_login_code_message_template: the effective fallback text this
|
||||
// admin process currently reads -- shown when the override above is empty.
|
||||
default_login_code_message_template: string;
|
||||
};
|
||||
|
||||
export type EnvField = {
|
||||
|
|
|
|||
|
|
@ -1401,6 +1401,8 @@ func run(logger *zap.Logger) error {
|
|||
)
|
||||
authService := auth.NewService(userStore, authzStore, codeStore, authKeyGetBatchStore, tempAuthKeyStore, cfg.DevAuthCode,
|
||||
auth.WithLoginMessages(messageStore, dialogStore),
|
||||
auth.WithLoginWelcomeMessages(identityStore, cfg.WelcomeMessagePhoneTemplate, cfg.WelcomeMessageEmailTemplate),
|
||||
auth.WithLoginCodeMessageTemplate(identityStore, cfg.LoginCodeMessageTemplate),
|
||||
auth.WithLoginCodeDelivery(messageStore),
|
||||
auth.WithPasswords(passwordStore),
|
||||
auth.WithBotLogin(botStore),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue