refactor: remove mode flag and introduce explicit demo flag

This commit is contained in:
Johnny 2026-01-20 22:58:33 +08:00
parent 05f31e457e
commit 47ebb04dc3
13 changed files with 79 additions and 96 deletions

View File

@ -25,7 +25,7 @@ var (
Short: `An open source, lightweight note-taking service. Easily capture and share your great thoughts.`,
Run: func(_ *cobra.Command, _ []string) {
instanceProfile := &profile.Profile{
Mode: viper.GetString("mode"),
Demo: viper.GetBool("demo"),
Addr: viper.GetString("addr"),
Port: viper.GetInt("port"),
UNIXSock: viper.GetString("unix-sock"),
@ -33,10 +33,12 @@ var (
Driver: viper.GetString("driver"),
DSN: viper.GetString("dsn"),
InstanceURL: viper.GetString("instance-url"),
Version: version.GetCurrentVersion(viper.GetString("mode")),
}
instanceProfile.Version = version.GetCurrentVersion()
if err := instanceProfile.Validate(); err != nil {
panic(err)
slog.Error("failed to validate profile", "error", err)
os.Exit(1)
}
ctx, cancel := context.WithCancel(context.Background())
@ -44,21 +46,21 @@ var (
if err != nil {
cancel()
slog.Error("failed to create db driver", "error", err)
return
os.Exit(1)
}
storeInstance := store.New(dbDriver, instanceProfile)
if err := storeInstance.Migrate(ctx); err != nil {
cancel()
slog.Error("failed to migrate", "error", err)
return
os.Exit(1)
}
s, err := server.NewServer(ctx, instanceProfile, storeInstance)
if err != nil {
cancel()
slog.Error("failed to create server", "error", err)
return
os.Exit(1)
}
c := make(chan os.Signal, 1)
@ -71,6 +73,7 @@ var (
if err != http.ErrServerClosed {
slog.Error("failed to start server", "error", err)
cancel()
os.Exit(1)
}
}
@ -89,11 +92,11 @@ var (
)
func init() {
viper.SetDefault("mode", "dev")
viper.SetDefault("demo", false)
viper.SetDefault("driver", "sqlite")
viper.SetDefault("port", 8081)
rootCmd.PersistentFlags().String("mode", "dev", `mode of server, can be "prod" or "dev" or "demo"`)
rootCmd.PersistentFlags().Bool("demo", false, "enable demo mode")
rootCmd.PersistentFlags().String("addr", "", "address of server")
rootCmd.PersistentFlags().Int("port", 8081, "port of server")
rootCmd.PersistentFlags().String("unix-sock", "", "path to the unix socket, overrides --addr and --port")
@ -102,7 +105,7 @@ func init() {
rootCmd.PersistentFlags().String("dsn", "", "database source name(aka. DSN)")
rootCmd.PersistentFlags().String("instance-url", "", "the url of your memos instance")
if err := viper.BindPFlag("mode", rootCmd.PersistentFlags().Lookup("mode")); err != nil {
if err := viper.BindPFlag("demo", rootCmd.PersistentFlags().Lookup("demo")); err != nil {
panic(err)
}
if err := viper.BindPFlag("addr", rootCmd.PersistentFlags().Lookup("addr")); err != nil {
@ -137,7 +140,7 @@ func init() {
func printGreetings(profile *profile.Profile) {
fmt.Printf("Memos %s started successfully!\n", profile.Version)
if profile.IsDev() {
if profile.Demo {
fmt.Fprint(os.Stderr, "Development mode is enabled\n")
if profile.DSN != "" {
fmt.Fprintf(os.Stderr, "Database: %s\n", profile.DSN)
@ -147,7 +150,6 @@ func printGreetings(profile *profile.Profile) {
// Server information
fmt.Printf("Data directory: %s\n", profile.Data)
fmt.Printf("Database driver: %s\n", profile.Driver)
fmt.Printf("Mode: %s\n", profile.Mode)
// Connection information
if len(profile.UNIXSock) == 0 {
@ -170,6 +172,6 @@ func printGreetings(profile *profile.Profile) {
func main() {
if err := rootCmd.Execute(); err != nil {
panic(err)
os.Exit(1)
}
}

View File

@ -13,8 +13,8 @@ import (
// Profile is the configuration to start main server.
type Profile struct {
// Mode can be "prod" or "dev" or "demo"
Mode string
// Demo indicates if the server is in demo mode
Demo bool
// Addr is the binding address for server
Addr string
// Port is the binding port for server
@ -34,10 +34,6 @@ type Profile struct {
InstanceURL string
}
func (p *Profile) IsDev() bool {
return p.Mode != "prod"
}
func checkDataDir(dataDir string) (string, error) {
// Convert to absolute path if relative path is supplied.
if !filepath.IsAbs(dataDir) {
@ -58,11 +54,7 @@ func checkDataDir(dataDir string) (string, error) {
}
func (p *Profile) Validate() error {
if p.Mode != "demo" && p.Mode != "dev" && p.Mode != "prod" {
p.Mode = "demo"
}
if p.Mode == "prod" && p.Data == "" {
if !p.Demo && p.Data == "" {
if runtime.GOOS == "windows" {
p.Data = filepath.Join(os.Getenv("ProgramData"), "memos")
if _, err := os.Stat(p.Data); os.IsNotExist(err) {
@ -84,7 +76,11 @@ func (p *Profile) Validate() error {
p.Data = dataDir
if p.Driver == "sqlite" && p.DSN == "" {
dbFile := fmt.Sprintf("memos_%s.db", p.Mode)
mode := "prod"
if p.Demo {
mode = "demo"
}
dbFile := fmt.Sprintf("memos_%s.db", mode)
p.DSN = filepath.Join(dataDir, dbFile)
}

View File

@ -11,13 +11,7 @@ import (
// Semantic versioning: https://semver.org/
var Version = "0.26.0"
// DevVersion is the service current development version.
var DevVersion = "0.26.0"
func GetCurrentVersion(mode string) string {
if mode == "dev" || mode == "demo" {
return DevVersion
}
func GetCurrentVersion() string {
return Version
}

View File

@ -41,8 +41,8 @@ message InstanceProfile {
// Version is the current version of instance.
string version = 2;
// Mode is the instance mode (e.g. "prod", "dev" or "demo").
string mode = 3;
// Demo indicates if the instance is in demo mode.
bool demo = 3;
// Instance URL is the URL of the instance.
string instance_url = 6;

View File

@ -143,8 +143,8 @@ type InstanceProfile struct {
Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"`
// Version is the current version of instance.
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
// Mode is the instance mode (e.g. "prod", "dev" or "demo").
Mode string `protobuf:"bytes,3,opt,name=mode,proto3" json:"mode,omitempty"`
// Demo indicates if the instance is in demo mode.
Demo bool `protobuf:"varint,3,opt,name=demo,proto3" json:"demo,omitempty"`
// Instance URL is the URL of the instance.
InstanceUrl string `protobuf:"bytes,6,opt,name=instance_url,json=instanceUrl,proto3" json:"instance_url,omitempty"`
unknownFields protoimpl.UnknownFields
@ -195,11 +195,11 @@ func (x *InstanceProfile) GetVersion() string {
return ""
}
func (x *InstanceProfile) GetMode() string {
func (x *InstanceProfile) GetDemo() bool {
if x != nil {
return x.Mode
return x.Demo
}
return ""
return false
}
func (x *InstanceProfile) GetInstanceUrl() string {
@ -879,7 +879,7 @@ const file_api_v1_instance_service_proto_rawDesc = "" +
"\x0fInstanceProfile\x12\x14\n" +
"\x05owner\x18\x01 \x01(\tR\x05owner\x12\x18\n" +
"\aversion\x18\x02 \x01(\tR\aversion\x12\x12\n" +
"\x04mode\x18\x03 \x01(\tR\x04mode\x12!\n" +
"\x04demo\x18\x03 \x01(\bR\x04demo\x12!\n" +
"\finstance_url\x18\x06 \x01(\tR\vinstanceUrl\"\x1b\n" +
"\x19GetInstanceProfileRequest\"\x99\x0f\n" +
"\x0fInstanceSetting\x12\x17\n" +

View File

@ -2140,9 +2140,9 @@ components:
version:
type: string
description: Version is the current version of instance.
mode:
type: string
description: Mode is the instance mode (e.g. "prod", "dev" or "demo").
demo:
type: boolean
description: Demo indicates if the instance is in demo mode.
instanceUrl:
type: string
description: Instance URL is the URL of the instance.

View File

@ -17,7 +17,7 @@ import (
func (s *APIV1Service) GetInstanceProfile(ctx context.Context, _ *v1pb.GetInstanceProfileRequest) (*v1pb.InstanceProfile, error) {
instanceProfile := &v1pb.InstanceProfile{
Version: s.Profile.Version,
Mode: s.Profile.Mode,
Demo: s.Profile.Demo,
InstanceUrl: s.Profile.InstanceURL,
}
owner, err := s.GetInstanceOwner(ctx)

View File

@ -126,7 +126,7 @@ func (s *APIV1Service) RegisterGateway(ctx context.Context, echoServer *echo.Ech
gwGroup.Any("/file/*", handler)
// Connect handlers for browser clients (replaces grpc-web).
logStacktraces := s.Profile.IsDev()
logStacktraces := s.Profile.Demo
connectInterceptors := connect.WithInterceptors(
NewMetadataInterceptor(), // Convert HTTP headers to gRPC metadata first
NewLoggingInterceptor(logStacktraces),

View File

@ -52,7 +52,7 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store
}
secret := "usememos"
if profile.Mode == "prod" {
if !profile.Demo {
secret = instanceBasicSetting.SecretKey
}
s.Secret = secret

View File

@ -57,10 +57,6 @@ const (
// defaultSchemaVersion is used when schema version is empty or not set.
// This handles edge cases for old installations without version tracking.
defaultSchemaVersion = "0.0.0"
// Mode constants for profile mode.
modeProd = "prod"
modeDemo = "demo"
)
// getSchemaVersionOrDefault returns the schema version or default if empty.
@ -110,38 +106,36 @@ func (s *Store) Migrate(ctx context.Context) error {
return errors.Wrap(err, "failed to pre-migrate")
}
switch s.profile.Mode {
case modeProd:
instanceBasicSetting, err := s.GetInstanceBasicSetting(ctx)
if err != nil {
return errors.Wrap(err, "failed to get instance basic setting")
instanceBasicSetting, err := s.GetInstanceBasicSetting(ctx)
if err != nil {
return errors.Wrap(err, "failed to get instance basic setting")
}
currentSchemaVersion, err := s.GetCurrentSchemaVersion()
if err != nil {
return errors.Wrap(err, "failed to get current schema version")
}
// Check for downgrade (but skip if schema version is empty - that means fresh/old installation)
if !isVersionEmpty(instanceBasicSetting.SchemaVersion) && version.IsVersionGreaterThan(instanceBasicSetting.SchemaVersion, currentSchemaVersion) {
slog.Error("cannot downgrade schema version",
slog.String("databaseVersion", instanceBasicSetting.SchemaVersion),
slog.String("currentVersion", currentSchemaVersion),
)
return errors.Errorf("cannot downgrade schema version from %s to %s", instanceBasicSetting.SchemaVersion, currentSchemaVersion)
}
// Apply migrations if needed (including when schema version is empty)
if isVersionEmpty(instanceBasicSetting.SchemaVersion) || version.IsVersionGreaterThan(currentSchemaVersion, instanceBasicSetting.SchemaVersion) {
if err := s.applyMigrations(ctx, instanceBasicSetting.SchemaVersion, currentSchemaVersion); err != nil {
return errors.Wrap(err, "failed to apply migrations")
}
currentSchemaVersion, err := s.GetCurrentSchemaVersion()
if err != nil {
return errors.Wrap(err, "failed to get current schema version")
}
// Check for downgrade (but skip if schema version is empty - that means fresh/old installation)
if !isVersionEmpty(instanceBasicSetting.SchemaVersion) && version.IsVersionGreaterThan(instanceBasicSetting.SchemaVersion, currentSchemaVersion) {
slog.Error("cannot downgrade schema version",
slog.String("databaseVersion", instanceBasicSetting.SchemaVersion),
slog.String("currentVersion", currentSchemaVersion),
)
return errors.Errorf("cannot downgrade schema version from %s to %s", instanceBasicSetting.SchemaVersion, currentSchemaVersion)
}
// Apply migrations if needed (including when schema version is empty)
if isVersionEmpty(instanceBasicSetting.SchemaVersion) || version.IsVersionGreaterThan(currentSchemaVersion, instanceBasicSetting.SchemaVersion) {
if err := s.applyMigrations(ctx, instanceBasicSetting.SchemaVersion, currentSchemaVersion); err != nil {
return errors.Wrap(err, "failed to apply migrations")
}
}
case modeDemo:
}
if s.profile.Demo {
// In demo mode, we should seed the database.
if err := s.seed(ctx); err != nil {
return errors.Wrap(err, "failed to seed")
}
default:
// For other modes (like dev), no special migration handling needed
}
return nil
}
@ -250,19 +244,16 @@ func (s *Store) preMigrate(ctx context.Context) error {
return errors.Wrap(err, "failed to get current schema version")
}
slog.Info("database initialized successfully", slog.String("schemaVersion", schemaVersion))
if err := s.updateCurrentSchemaVersion(ctx, schemaVersion); err != nil {
return errors.Wrap(err, "failed to update current schema version")
if err := s.updateCurrentSchemaVersion(ctx, schemaVersion); err != nil {
return errors.Wrap(err, "failed to update current schema version")
}
}
if err := s.checkMinimumUpgradeVersion(ctx); err != nil {
return err // Error message is already descriptive, don't wrap it
}
return nil
}
}
if s.profile.Mode == modeProd {
if err := s.checkMinimumUpgradeVersion(ctx); err != nil {
return err // Error message is already descriptive, don't wrap it
}
}
return nil
}
func (s *Store) getMigrationBasePath() string {
return fmt.Sprintf("migration/%s/", s.profile.Driver)
}
@ -308,7 +299,7 @@ func (s *Store) seed(ctx context.Context) error {
}
func (s *Store) GetCurrentSchemaVersion() (string, error) {
currentVersion := version.GetCurrentVersion(s.profile.Mode)
currentVersion := version.GetCurrentVersion()
minorVersion := version.GetMinorVersion(currentVersion)
filePaths, err := fs.Glob(migrationFS, fmt.Sprintf("%s%s/*.sql", s.getMigrationBasePath(), minorVersion))
if err != nil {

View File

@ -19,8 +19,8 @@ function PasswordSignInForm() {
const { profile } = useInstance();
const { initialize } = useAuth();
const actionBtnLoadingState = useLoading(false);
const [username, setUsername] = useState(profile.mode === "demo" ? "demo" : "");
const [password, setPassword] = useState(profile.mode === "demo" ? "secret" : "");
const [username, setUsername] = useState(profile.demo ? "demo" : "");
const [password, setPassword] = useState(profile.demo ? "secret" : "");
const handleUsernameInputChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
const text = e.target.value as string;

View File

@ -112,7 +112,7 @@ const InstanceSection = () => {
<SettingGroup>
<SettingRow label={t("setting.instance-section.disallow-user-registration")}>
<Switch
disabled={profile.mode === "demo"}
disabled={profile.demo}
checked={instanceGeneralSetting.disallowUserRegistration}
onCheckedChange={(checked) => updatePartialSetting({ disallowUserRegistration: checked })}
/>
@ -120,7 +120,7 @@ const InstanceSection = () => {
<SettingRow label={t("setting.instance-section.disallow-password-auth")}>
<Switch
disabled={profile.mode === "demo" || (identityProviderList.length === 0 && !instanceGeneralSetting.disallowPasswordAuth)}
disabled={profile.demo || (identityProviderList.length === 0 && !instanceGeneralSetting.disallowPasswordAuth)}
checked={instanceGeneralSetting.disallowPasswordAuth}
onCheckedChange={(checked) => updatePartialSetting({ disallowPasswordAuth: checked })}
/>

View File

@ -16,7 +16,7 @@ import type { Message } from "@bufbuild/protobuf";
* Describes the file api/v1/instance_service.proto.
*/
export const file_api_v1_instance_service: GenFile = /*@__PURE__*/
fileDesc("Ch1hcGkvdjEvaW5zdGFuY2Vfc2VydmljZS5wcm90bxIMbWVtb3MuYXBpLnYxIlUKD0luc3RhbmNlUHJvZmlsZRINCgVvd25lchgBIAEoCRIPCgd2ZXJzaW9uGAIgASgJEgwKBG1vZGUYAyABKAkSFAoMaW5zdGFuY2VfdXJsGAYgASgJIhsKGUdldEluc3RhbmNlUHJvZmlsZVJlcXVlc3QiswsKD0luc3RhbmNlU2V0dGluZxIRCgRuYW1lGAEgASgJQgPgQQgSRwoPZ2VuZXJhbF9zZXR0aW5nGAIgASgLMiwubWVtb3MuYXBpLnYxLkluc3RhbmNlU2V0dGluZy5HZW5lcmFsU2V0dGluZ0gAEkcKD3N0b3JhZ2Vfc2V0dGluZxgDIAEoCzIsLm1lbW9zLmFwaS52MS5JbnN0YW5jZVNldHRpbmcuU3RvcmFnZVNldHRpbmdIABJQChRtZW1vX3JlbGF0ZWRfc2V0dGluZxgEIAEoCzIwLm1lbW9zLmFwaS52MS5JbnN0YW5jZVNldHRpbmcuTWVtb1JlbGF0ZWRTZXR0aW5nSAAahwMKDkdlbmVyYWxTZXR0aW5nEiIKGmRpc2FsbG93X3VzZXJfcmVnaXN0cmF0aW9uGAIgASgIEh4KFmRpc2FsbG93X3Bhc3N3b3JkX2F1dGgYAyABKAgSGQoRYWRkaXRpb25hbF9zY3JpcHQYBCABKAkSGAoQYWRkaXRpb25hbF9zdHlsZRgFIAEoCRJSCg5jdXN0b21fcHJvZmlsZRgGIAEoCzI6Lm1lbW9zLmFwaS52MS5JbnN0YW5jZVNldHRpbmcuR2VuZXJhbFNldHRpbmcuQ3VzdG9tUHJvZmlsZRIdChV3ZWVrX3N0YXJ0X2RheV9vZmZzZXQYByABKAUSIAoYZGlzYWxsb3dfY2hhbmdlX3VzZXJuYW1lGAggASgIEiAKGGRpc2FsbG93X2NoYW5nZV9uaWNrbmFtZRgJIAEoCBpFCg1DdXN0b21Qcm9maWxlEg0KBXRpdGxlGAEgASgJEhMKC2Rlc2NyaXB0aW9uGAIgASgJEhAKCGxvZ29fdXJsGAMgASgJGroDCg5TdG9yYWdlU2V0dGluZxJOCgxzdG9yYWdlX3R5cGUYASABKA4yOC5tZW1vcy5hcGkudjEuSW5zdGFuY2VTZXR0aW5nLlN0b3JhZ2VTZXR0aW5nLlN0b3JhZ2VUeXBlEhkKEWZpbGVwYXRoX3RlbXBsYXRlGAIgASgJEhwKFHVwbG9hZF9zaXplX2xpbWl0X21iGAMgASgDEkgKCXMzX2NvbmZpZxgEIAEoCzI1Lm1lbW9zLmFwaS52MS5JbnN0YW5jZVNldHRpbmcuU3RvcmFnZVNldHRpbmcuUzNDb25maWcahgEKCFMzQ29uZmlnEhUKDWFjY2Vzc19rZXlfaWQYASABKAkSGQoRYWNjZXNzX2tleV9zZWNyZXQYAiABKAkSEAoIZW5kcG9pbnQYAyABKAkSDgoGcmVnaW9uGAQgASgJEg4KBmJ1Y2tldBgFIAEoCRIWCg51c2VfcGF0aF9zdHlsZRgGIAEoCCJMCgtTdG9yYWdlVHlwZRIcChhTVE9SQUdFX1RZUEVfVU5TUEVDSUZJRUQQABIMCghEQVRBQkFTRRABEgkKBUxPQ0FMEAISBgoCUzMQAxqtAQoSTWVtb1JlbGF0ZWRTZXR0aW5nEiIKGmRpc2FsbG93X3B1YmxpY192aXNpYmlsaXR5GAEgASgIEiAKGGRpc3BsYXlfd2l0aF91cGRhdGVfdGltZRgCIAEoCBIcChRjb250ZW50X2xlbmd0aF9saW1pdBgDIAEoBRIgChhlbmFibGVfZG91YmxlX2NsaWNrX2VkaXQYBCABKAgSEQoJcmVhY3Rpb25zGAcgAygJIkYKA0tleRITCg9LRVlfVU5TUEVDSUZJRUQQABILCgdHRU5FUkFMEAESCwoHU1RPUkFHRRACEhAKDE1FTU9fUkVMQVRFRBADOmHqQV4KHG1lbW9zLmFwaS52MS9JbnN0YW5jZVNldHRpbmcSG2luc3RhbmNlL3NldHRpbmdzL3tzZXR0aW5nfSoQaW5zdGFuY2VTZXR0aW5nczIPaW5zdGFuY2VTZXR0aW5nQgcKBXZhbHVlIk8KGUdldEluc3RhbmNlU2V0dGluZ1JlcXVlc3QSMgoEbmFtZRgBIAEoCUIk4EEC+kEeChxtZW1vcy5hcGkudjEvSW5zdGFuY2VTZXR0aW5nIokBChxVcGRhdGVJbnN0YW5jZVNldHRpbmdSZXF1ZXN0EjMKB3NldHRpbmcYASABKAsyHS5tZW1vcy5hcGkudjEuSW5zdGFuY2VTZXR0aW5nQgPgQQISNAoLdXBkYXRlX21hc2sYAiABKAsyGi5nb29nbGUucHJvdG9idWYuRmllbGRNYXNrQgPgQQEy2wMKD0luc3RhbmNlU2VydmljZRJ+ChJHZXRJbnN0YW5jZVByb2ZpbGUSJy5tZW1vcy5hcGkudjEuR2V0SW5zdGFuY2VQcm9maWxlUmVxdWVzdBodLm1lbW9zLmFwaS52MS5JbnN0YW5jZVByb2ZpbGUiIILT5JMCGhIYL2FwaS92MS9pbnN0YW5jZS9wcm9maWxlEo8BChJHZXRJbnN0YW5jZVNldHRpbmcSJy5tZW1vcy5hcGkudjEuR2V0SW5zdGFuY2VTZXR0aW5nUmVxdWVzdBodLm1lbW9zLmFwaS52MS5JbnN0YW5jZVNldHRpbmciMdpBBG5hbWWC0+STAiQSIi9hcGkvdjEve25hbWU9aW5zdGFuY2Uvc2V0dGluZ3MvKn0StQEKFVVwZGF0ZUluc3RhbmNlU2V0dGluZxIqLm1lbW9zLmFwaS52MS5VcGRhdGVJbnN0YW5jZVNldHRpbmdSZXF1ZXN0Gh0ubWVtb3MuYXBpLnYxLkluc3RhbmNlU2V0dGluZyJR2kETc2V0dGluZyx1cGRhdGVfbWFza4LT5JMCNToHc2V0dGluZzIqL2FwaS92MS97c2V0dGluZy5uYW1lPWluc3RhbmNlL3NldHRpbmdzLyp9QqwBChBjb20ubWVtb3MuYXBpLnYxQhRJbnN0YW5jZVNlcnZpY2VQcm90b1ABWjBnaXRodWIuY29tL3VzZW1lbW9zL21lbW9zL3Byb3RvL2dlbi9hcGkvdjE7YXBpdjGiAgNNQViqAgxNZW1vcy5BcGkuVjHKAgxNZW1vc1xBcGlcVjHiAhhNZW1vc1xBcGlcVjFcR1BCTWV0YWRhdGHqAg5NZW1vczo6QXBpOjpWMWIGcHJvdG8z", [file_google_api_annotations, file_google_api_client, file_google_api_field_behavior, file_google_api_resource, file_google_protobuf_field_mask]);
fileDesc("Ch1hcGkvdjEvaW5zdGFuY2Vfc2VydmljZS5wcm90bxIMbWVtb3MuYXBpLnYxIlUKD0luc3RhbmNlUHJvZmlsZRINCgVvd25lchgBIAEoCRIPCgd2ZXJzaW9uGAIgASgJEgwKBGRlbW8YAyABKAgSFAoMaW5zdGFuY2VfdXJsGAYgASgJIhsKGUdldEluc3RhbmNlUHJvZmlsZVJlcXVlc3QiswsKD0luc3RhbmNlU2V0dGluZxIRCgRuYW1lGAEgASgJQgPgQQgSRwoPZ2VuZXJhbF9zZXR0aW5nGAIgASgLMiwubWVtb3MuYXBpLnYxLkluc3RhbmNlU2V0dGluZy5HZW5lcmFsU2V0dGluZ0gAEkcKD3N0b3JhZ2Vfc2V0dGluZxgDIAEoCzIsLm1lbW9zLmFwaS52MS5JbnN0YW5jZVNldHRpbmcuU3RvcmFnZVNldHRpbmdIABJQChRtZW1vX3JlbGF0ZWRfc2V0dGluZxgEIAEoCzIwLm1lbW9zLmFwaS52MS5JbnN0YW5jZVNldHRpbmcuTWVtb1JlbGF0ZWRTZXR0aW5nSAAahwMKDkdlbmVyYWxTZXR0aW5nEiIKGmRpc2FsbG93X3VzZXJfcmVnaXN0cmF0aW9uGAIgASgIEh4KFmRpc2FsbG93X3Bhc3N3b3JkX2F1dGgYAyABKAgSGQoRYWRkaXRpb25hbF9zY3JpcHQYBCABKAkSGAoQYWRkaXRpb25hbF9zdHlsZRgFIAEoCRJSCg5jdXN0b21fcHJvZmlsZRgGIAEoCzI6Lm1lbW9zLmFwaS52MS5JbnN0YW5jZVNldHRpbmcuR2VuZXJhbFNldHRpbmcuQ3VzdG9tUHJvZmlsZRIdChV3ZWVrX3N0YXJ0X2RheV9vZmZzZXQYByABKAUSIAoYZGlzYWxsb3dfY2hhbmdlX3VzZXJuYW1lGAggASgIEiAKGGRpc2FsbG93X2NoYW5nZV9uaWNrbmFtZRgJIAEoCBpFCg1DdXN0b21Qcm9maWxlEg0KBXRpdGxlGAEgASgJEhMKC2Rlc2NyaXB0aW9uGAIgASgJEhAKCGxvZ29fdXJsGAMgASgJGroDCg5TdG9yYWdlU2V0dGluZxJOCgxzdG9yYWdlX3R5cGUYASABKA4yOC5tZW1vcy5hcGkudjEuSW5zdGFuY2VTZXR0aW5nLlN0b3JhZ2VTZXR0aW5nLlN0b3JhZ2VUeXBlEhkKEWZpbGVwYXRoX3RlbXBsYXRlGAIgASgJEhwKFHVwbG9hZF9zaXplX2xpbWl0X21iGAMgASgDEkgKCXMzX2NvbmZpZxgEIAEoCzI1Lm1lbW9zLmFwaS52MS5JbnN0YW5jZVNldHRpbmcuU3RvcmFnZVNldHRpbmcuUzNDb25maWcahgEKCFMzQ29uZmlnEhUKDWFjY2Vzc19rZXlfaWQYASABKAkSGQoRYWNjZXNzX2tleV9zZWNyZXQYAiABKAkSEAoIZW5kcG9pbnQYAyABKAkSDgoGcmVnaW9uGAQgASgJEg4KBmJ1Y2tldBgFIAEoCRIWCg51c2VfcGF0aF9zdHlsZRgGIAEoCCJMCgtTdG9yYWdlVHlwZRIcChhTVE9SQUdFX1RZUEVfVU5TUEVDSUZJRUQQABIMCghEQVRBQkFTRRABEgkKBUxPQ0FMEAISBgoCUzMQAxqtAQoSTWVtb1JlbGF0ZWRTZXR0aW5nEiIKGmRpc2FsbG93X3B1YmxpY192aXNpYmlsaXR5GAEgASgIEiAKGGRpc3BsYXlfd2l0aF91cGRhdGVfdGltZRgCIAEoCBIcChRjb250ZW50X2xlbmd0aF9saW1pdBgDIAEoBRIgChhlbmFibGVfZG91YmxlX2NsaWNrX2VkaXQYBCABKAgSEQoJcmVhY3Rpb25zGAcgAygJIkYKA0tleRITCg9LRVlfVU5TUEVDSUZJRUQQABILCgdHRU5FUkFMEAESCwoHU1RPUkFHRRACEhAKDE1FTU9fUkVMQVRFRBADOmHqQV4KHG1lbW9zLmFwaS52MS9JbnN0YW5jZVNldHRpbmcSG2luc3RhbmNlL3NldHRpbmdzL3tzZXR0aW5nfSoQaW5zdGFuY2VTZXR0aW5nczIPaW5zdGFuY2VTZXR0aW5nQgcKBXZhbHVlIk8KGUdldEluc3RhbmNlU2V0dGluZ1JlcXVlc3QSMgoEbmFtZRgBIAEoCUIk4EEC+kEeChxtZW1vcy5hcGkudjEvSW5zdGFuY2VTZXR0aW5nIokBChxVcGRhdGVJbnN0YW5jZVNldHRpbmdSZXF1ZXN0EjMKB3NldHRpbmcYASABKAsyHS5tZW1vcy5hcGkudjEuSW5zdGFuY2VTZXR0aW5nQgPgQQISNAoLdXBkYXRlX21hc2sYAiABKAsyGi5nb29nbGUucHJvdG9idWYuRmllbGRNYXNrQgPgQQEy2wMKD0luc3RhbmNlU2VydmljZRJ+ChJHZXRJbnN0YW5jZVByb2ZpbGUSJy5tZW1vcy5hcGkudjEuR2V0SW5zdGFuY2VQcm9maWxlUmVxdWVzdBodLm1lbW9zLmFwaS52MS5JbnN0YW5jZVByb2ZpbGUiIILT5JMCGhIYL2FwaS92MS9pbnN0YW5jZS9wcm9maWxlEo8BChJHZXRJbnN0YW5jZVNldHRpbmcSJy5tZW1vcy5hcGkudjEuR2V0SW5zdGFuY2VTZXR0aW5nUmVxdWVzdBodLm1lbW9zLmFwaS52MS5JbnN0YW5jZVNldHRpbmciMdpBBG5hbWWC0+STAiQSIi9hcGkvdjEve25hbWU9aW5zdGFuY2Uvc2V0dGluZ3MvKn0StQEKFVVwZGF0ZUluc3RhbmNlU2V0dGluZxIqLm1lbW9zLmFwaS52MS5VcGRhdGVJbnN0YW5jZVNldHRpbmdSZXF1ZXN0Gh0ubWVtb3MuYXBpLnYxLkluc3RhbmNlU2V0dGluZyJR2kETc2V0dGluZyx1cGRhdGVfbWFza4LT5JMCNToHc2V0dGluZzIqL2FwaS92MS97c2V0dGluZy5uYW1lPWluc3RhbmNlL3NldHRpbmdzLyp9QqwBChBjb20ubWVtb3MuYXBpLnYxQhRJbnN0YW5jZVNlcnZpY2VQcm90b1ABWjBnaXRodWIuY29tL3VzZW1lbW9zL21lbW9zL3Byb3RvL2dlbi9hcGkvdjE7YXBpdjGiAgNNQViqAgxNZW1vcy5BcGkuVjHKAgxNZW1vc1xBcGlcVjHiAhhNZW1vc1xBcGlcVjFcR1BCTWV0YWRhdGHqAg5NZW1vczo6QXBpOjpWMWIGcHJvdG8z", [file_google_api_annotations, file_google_api_client, file_google_api_field_behavior, file_google_api_resource, file_google_protobuf_field_mask]);
/**
* Instance profile message containing basic instance information.
@ -40,11 +40,11 @@ export type InstanceProfile = Message<"memos.api.v1.InstanceProfile"> & {
version: string;
/**
* Mode is the instance mode (e.g. "prod", "dev" or "demo").
* Demo indicates if the instance is in demo mode.
*
* @generated from field: string mode = 3;
* @generated from field: bool demo = 3;
*/
mode: string;
demo: boolean;
/**
* Instance URL is the URL of the instance.