start method

Future<void> start()

Starts the Serverpod and all Servers that it manages.

Implementation

Future<void> start() async {
  _startedTime = DateTime.now().toUtc();

  await runZonedGuarded(() async {
    // Register cloud store endpoint if we're using the database cloud store
    if (storage['public'] is DatabaseCloudStorage ||
        storage['private'] is DatabaseCloudStorage) {
      CloudStoragePublicEndpoint().register(this);
    }

    int? maxAttempts =
        commandLineArgs.role == ServerpodRole.maintenance ? 6 : null;

    Session session;

    try {
      session = await _connectToDatabase(
        enableLogging: false,
        maxAttempts: maxAttempts,
      );
    } catch (e) {
      _exitCode = 1;
      stderr.writeln(
        'Failed to connect to the database. $e',
      );
      exit(_exitCode);
    }

    try {
      logVerbose('Initializing migration manager.');
      migrationManager = MigrationManager();
      await migrationManager.initialize(session);

      if (commandLineArgs.applyRepairMigration) {
        logVerbose('Applying database repair migration');
        var appliedRepairMigration =
            await migrationManager.applyRepairMigration(session);
        if (appliedRepairMigration == null) {
          stderr.writeln('Failed to apply database repair migration.');
        } else {
          stdout.writeln(
              'Database repair migration "$appliedRepairMigration" applied.');
        }
        await migrationManager.initialize(session);
      }

      if (commandLineArgs.applyMigrations) {
        logVerbose('Applying database migrations.');
        var migrationsApplied =
            await migrationManager.migrateToLatest(session);

        if (migrationsApplied == null) {
          stdout.writeln('Latest database migration already applied.');
        } else {
          stdout.writeln(
              'Applied database migration${migrationsApplied.length > 1 ? 's' : ''}:');
          for (var migration in migrationsApplied) {
            stdout.writeln(' - $migration');
          }
        }

        await migrationManager.initialize(session);
      }

      logVerbose('Verifying database integrity.');
      await migrationManager.verifyDatabaseIntegrity(session);
    } catch (e) {
      _exitCode = 1;
      stderr.writeln(
        'Failed to apply database migrations. $e',
      );
    }

    logVerbose('Loading runtime settings.');
    try {
      _runtimeSettings =
          await internal.RuntimeSettings.db.findFirstRow(session);
    } catch (e) {
      _exitCode = 1;
      stderr.writeln(
        'Failed to load runtime settings. $e',
      );
    }

    if (_runtimeSettings == null) {
      logVerbose('Runtime settings not found, creating default settings.');
      try {
        _runtimeSettings = await RuntimeSettings.db
            .insertRow(session, _defaultRuntimeSettings);
      } catch (e) {
        _exitCode = 1;
        stderr.writeln(
          'Failed to store runtime settings. $e',
        );
      }
    } else {
      logVerbose('Runtime settings loaded.');
    }

    await session.close();

    // Setup log manager.
    _logManager = LogManager(_runtimeSettings ?? _defaultRuntimeSettings);

    // Connect to Redis
    if (redisController != null) {
      logVerbose('Connecting to Redis.');
      await redisController!.start();
    } else {
      logVerbose('Redis is disabled, skipping.');
    }

    // Start servers.
    if (commandLineArgs.role == ServerpodRole.monolith ||
        commandLineArgs.role == ServerpodRole.serverless) {
      var serversStarted = true;

      // Serverpod Insights.
      if (_isValidSecret(config.serviceSecret)) {
        serversStarted &= await _startInsightsServer();
      } else {
        stderr.write(
          'Invalid serviceSecret in password file, Insights server disabled.',
        );
      }

      // Main API server.
      serversStarted &= await server.start();

      /// Web server.
      if (webServer.routes.isNotEmpty) {
        logVerbose('Starting web server.');
        serversStarted &= await webServer.start();
      } else {
        logVerbose('No routes configured for web server, skipping.');
      }

      if (!serversStarted) {
        _exitCode = 1;
        stderr.writeln('Failed to start servers.');
        exit(_exitCode);
      }

      logVerbose('All servers started.');
    }

    // Start maintenance tasks. If we are running in maintenance mode, we
    // will only run the maintenance tasks once. If we are applying migrations
    // no other maintenance tasks will be run.
    var appliedMigrations = (commandLineArgs.applyMigrations |
        commandLineArgs.applyRepairMigration);
    if (commandLineArgs.role == ServerpodRole.monolith ||
        (commandLineArgs.role == ServerpodRole.maintenance &&
            !appliedMigrations)) {
      logVerbose('Starting maintenance tasks.');

      // Start future calls
      _futureCallManager.start();

      // Start health check manager
      await _healthCheckManager.start();
    }

    logVerbose('Serverpod start complete.');

    if (commandLineArgs.role == ServerpodRole.maintenance &&
        appliedMigrations) {
      logVerbose('Finished applying database migrations.');
      exit(_exitCode);
    }
  }, (e, stackTrace) {
    _exitCode = 1;
    // Last resort error handling
    // TODO: Log to database?
    stderr.writeln(
      '${DateTime.now().toUtc()} Internal server error. Zoned exception.',
    );
    stderr.writeln('$e');
    stderr.writeln('$stackTrace');
  });
}