feat(todo): the server on halkyon_base
Todo's server includes halkyon_base (ADR 0014), configured for Todo: its basics say it serves `todo`, sign-in mails name Halkyon Todo, the source is this repository, minimumAppBuild 1. Authentication through the module (anonymous, GitHub, email codes, server-side sessions) in place of the scaffold's email-and-password provider; the module's pruning scheduled once however often the server starts. The scaffold's greeting, its Flutter web hosting, cloud storage and Docker files go; session logs are never persisted. Todo was never deployed, so its migrations start again with one initial migration. The root's lints apply, with the four constructor rules off as on Learn's server. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
38770569ea
commit
28dd3ea65a
47 changed files with 1047 additions and 1741 deletions
|
|
@ -10,9 +10,7 @@
|
|||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:async' as _ida;
|
||||
import 'package:halkyon_todo_client/src/protocol/greetings/greeting.dart'
|
||||
as _i5fdz6wc;
|
||||
import 'package:halkyon_base_client/halkyon_base_client.dart' as _ihk4p5t7;
|
||||
import 'package:http/http.dart' as _i85jenna;
|
||||
import 'package:serverpod_auth_core_client/serverpod_auth_core_client.dart'
|
||||
as _iacc;
|
||||
|
|
@ -21,213 +19,15 @@ import 'package:serverpod_auth_idp_client/serverpod_auth_idp_client.dart'
|
|||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
import 'protocol.dart' as _il2as5qe;
|
||||
|
||||
/// By extending [EmailIdpBaseEndpoint], the email identity provider endpoints
|
||||
/// are made available on the server and enable the corresponding sign-in widget
|
||||
/// on the client.
|
||||
/// {@category Endpoint}
|
||||
class EndpointEmailIdp extends _iaic.EndpointEmailIdpBase {
|
||||
EndpointEmailIdp(_isc.EndpointCaller caller) : super(caller);
|
||||
|
||||
@override
|
||||
String get name => 'emailIdp';
|
||||
|
||||
/// Logs in the user and returns a new session.
|
||||
///
|
||||
/// Throws an [EmailAccountLoginException] in case of errors, with reason:
|
||||
/// - [EmailAccountLoginExceptionReason.invalidCredentials] if the email or
|
||||
/// password is incorrect.
|
||||
/// - [EmailAccountLoginExceptionReason.tooManyAttempts] if there have been
|
||||
/// too many failed login attempts.
|
||||
///
|
||||
/// Throws an [AuthUserBlockedException] if the auth user is blocked.
|
||||
@override
|
||||
_ida.Future<_iacc.AuthSuccess> login({
|
||||
required String email,
|
||||
required String password,
|
||||
}) => caller.callServerEndpoint<_iacc.AuthSuccess>(
|
||||
'emailIdp',
|
||||
'login',
|
||||
{
|
||||
'email': email,
|
||||
'password': password,
|
||||
},
|
||||
);
|
||||
|
||||
/// Starts the registration for a new user account with an email-based login
|
||||
/// associated to it.
|
||||
///
|
||||
/// Upon successful completion of this method, an email will have been
|
||||
/// sent to [email] with a verification link, which the user must open to
|
||||
/// complete the registration.
|
||||
///
|
||||
/// Always returns a account request ID, which can be used to complete the
|
||||
/// registration. If the email is already registered, the returned ID will not
|
||||
/// be valid.
|
||||
@override
|
||||
_ida.Future<_isc.UuidValue> startRegistration({required String email}) =>
|
||||
caller.callServerEndpoint<_isc.UuidValue>(
|
||||
'emailIdp',
|
||||
'startRegistration',
|
||||
{'email': email},
|
||||
);
|
||||
|
||||
/// Verifies an account request code and returns a token
|
||||
/// that can be used to complete the account creation.
|
||||
///
|
||||
/// Throws an [EmailAccountRequestException] in case of errors, with reason:
|
||||
/// - [EmailAccountRequestExceptionReason.expired] if the account request has
|
||||
/// already expired.
|
||||
/// - [EmailAccountRequestExceptionReason.policyViolation] if the password
|
||||
/// does not comply with the password policy.
|
||||
/// - [EmailAccountRequestExceptionReason.invalid] if no request exists
|
||||
/// for the given [accountRequestId] or [verificationCode] is invalid.
|
||||
@override
|
||||
_ida.Future<String> verifyRegistrationCode({
|
||||
required _isc.UuidValue accountRequestId,
|
||||
required String verificationCode,
|
||||
}) => caller.callServerEndpoint<String>(
|
||||
'emailIdp',
|
||||
'verifyRegistrationCode',
|
||||
{
|
||||
'accountRequestId': accountRequestId,
|
||||
'verificationCode': verificationCode,
|
||||
},
|
||||
);
|
||||
|
||||
/// Completes a new account registration, creating a new auth user with a
|
||||
/// profile and attaching the given email account to it.
|
||||
///
|
||||
/// Throws an [EmailAccountRequestException] in case of errors, with reason:
|
||||
/// - [EmailAccountRequestExceptionReason.expired] if the account request has
|
||||
/// already expired.
|
||||
/// - [EmailAccountRequestExceptionReason.policyViolation] if the password
|
||||
/// does not comply with the password policy.
|
||||
/// - [EmailAccountRequestExceptionReason.invalid] if the [registrationToken]
|
||||
/// is invalid.
|
||||
///
|
||||
/// Throws an [AuthUserBlockedException] if the auth user is blocked.
|
||||
///
|
||||
/// Returns a session for the newly created user.
|
||||
@override
|
||||
_ida.Future<_iacc.AuthSuccess> finishRegistration({
|
||||
required String registrationToken,
|
||||
required String password,
|
||||
}) => caller.callServerEndpoint<_iacc.AuthSuccess>(
|
||||
'emailIdp',
|
||||
'finishRegistration',
|
||||
{
|
||||
'registrationToken': registrationToken,
|
||||
'password': password,
|
||||
},
|
||||
);
|
||||
|
||||
/// Requests a password reset for [email].
|
||||
///
|
||||
/// If the email address is registered, an email with reset instructions will
|
||||
/// be send out. If the email is unknown, this method will have no effect.
|
||||
///
|
||||
/// Always returns a password reset request ID, which can be used to complete
|
||||
/// the reset. If the email is not registered, the returned ID will not be
|
||||
/// valid.
|
||||
///
|
||||
/// Throws an [EmailAccountPasswordResetException] in case of errors, with reason:
|
||||
/// - [EmailAccountPasswordResetExceptionReason.tooManyAttempts] if the user has
|
||||
/// made too many attempts trying to request a password reset.
|
||||
///
|
||||
@override
|
||||
_ida.Future<_isc.UuidValue> startPasswordReset({required String email}) =>
|
||||
caller.callServerEndpoint<_isc.UuidValue>(
|
||||
'emailIdp',
|
||||
'startPasswordReset',
|
||||
{'email': email},
|
||||
);
|
||||
|
||||
/// Verifies a password reset code and returns a finishPasswordResetToken
|
||||
/// that can be used to finish the password reset.
|
||||
///
|
||||
/// Throws an [EmailAccountPasswordResetException] in case of errors, with reason:
|
||||
/// - [EmailAccountPasswordResetExceptionReason.expired] if the password reset
|
||||
/// request has already expired.
|
||||
/// - [EmailAccountPasswordResetExceptionReason.tooManyAttempts] if the user has
|
||||
/// made too many attempts trying to verify the password reset.
|
||||
/// - [EmailAccountPasswordResetExceptionReason.invalid] if no request exists
|
||||
/// for the given [passwordResetRequestId] or [verificationCode] is invalid.
|
||||
///
|
||||
/// If multiple steps are required to complete the password reset, this endpoint
|
||||
/// should be overridden to return credentials for the next step instead
|
||||
/// of the credentials for setting the password.
|
||||
@override
|
||||
_ida.Future<String> verifyPasswordResetCode({
|
||||
required _isc.UuidValue passwordResetRequestId,
|
||||
required String verificationCode,
|
||||
}) => caller.callServerEndpoint<String>(
|
||||
'emailIdp',
|
||||
'verifyPasswordResetCode',
|
||||
{
|
||||
'passwordResetRequestId': passwordResetRequestId,
|
||||
'verificationCode': verificationCode,
|
||||
},
|
||||
);
|
||||
|
||||
/// Completes a password reset request by setting a new password.
|
||||
///
|
||||
/// The [verificationCode] returned from [verifyPasswordResetCode] is used to
|
||||
/// validate the password reset request.
|
||||
///
|
||||
/// Throws an [EmailAccountPasswordResetException] in case of errors, with reason:
|
||||
/// - [EmailAccountPasswordResetExceptionReason.expired] if the password reset
|
||||
/// request has already expired.
|
||||
/// - [EmailAccountPasswordResetExceptionReason.policyViolation] if the new
|
||||
/// password does not comply with the password policy.
|
||||
/// - [EmailAccountPasswordResetExceptionReason.invalid] if no request exists
|
||||
/// for the given [passwordResetRequestId] or [verificationCode] is invalid.
|
||||
///
|
||||
/// Throws an [AuthUserBlockedException] if the auth user is blocked.
|
||||
@override
|
||||
_ida.Future<void> finishPasswordReset({
|
||||
required String finishPasswordResetToken,
|
||||
required String newPassword,
|
||||
}) => caller.callServerEndpoint<void>(
|
||||
'emailIdp',
|
||||
'finishPasswordReset',
|
||||
{
|
||||
'finishPasswordResetToken': finishPasswordResetToken,
|
||||
'newPassword': newPassword,
|
||||
},
|
||||
);
|
||||
|
||||
@override
|
||||
_ida.Future<bool> hasAccount() => caller.callServerEndpoint<bool>(
|
||||
'emailIdp',
|
||||
'hasAccount',
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
/// This is an example endpoint that returns a greeting message through
|
||||
/// its [hello] method.
|
||||
/// {@category Endpoint}
|
||||
class EndpointGreeting extends _isc.EndpointRef {
|
||||
EndpointGreeting(_isc.EndpointCaller caller) : super(caller);
|
||||
|
||||
@override
|
||||
String get name => 'greeting';
|
||||
|
||||
/// Returns a personalized greeting message: "Hello {name}".
|
||||
_ida.Future<_i5fdz6wc.Greeting> hello(String name) =>
|
||||
caller.callServerEndpoint<_i5fdz6wc.Greeting>(
|
||||
'greeting',
|
||||
'hello',
|
||||
{'name': name},
|
||||
);
|
||||
}
|
||||
|
||||
class Modules {
|
||||
Modules(Client client) {
|
||||
base = _ihk4p5t7.Caller(client);
|
||||
serverpod_auth_idp = _iaic.Caller(client);
|
||||
serverpod_auth_core = _iacc.Caller(client);
|
||||
}
|
||||
|
||||
late final _ihk4p5t7.Caller base;
|
||||
|
||||
late final _iaic.Caller serverpod_auth_idp;
|
||||
|
||||
late final _iacc.Caller serverpod_auth_core;
|
||||
|
|
@ -260,25 +60,17 @@ class Client extends _isc.ServerpodClientShared {
|
|||
disconnectStreamsOnLostInternetConnection,
|
||||
httpClientOverride: httpClientOverride,
|
||||
) {
|
||||
emailIdp = EndpointEmailIdp(this);
|
||||
greeting = EndpointGreeting(this);
|
||||
modules = Modules(this);
|
||||
}
|
||||
|
||||
late final EndpointEmailIdp emailIdp;
|
||||
|
||||
late final EndpointGreeting greeting;
|
||||
|
||||
late final Modules modules;
|
||||
|
||||
@override
|
||||
Map<String, _isc.EndpointRef> get endpointRefLookup => {
|
||||
'emailIdp': emailIdp,
|
||||
'greeting': greeting,
|
||||
};
|
||||
Map<String, _isc.EndpointRef> get endpointRefLookup => {};
|
||||
|
||||
@override
|
||||
Map<String, _isc.ModuleEndpointCaller> get moduleLookup => {
|
||||
'base': modules.base,
|
||||
'serverpod_auth_idp': modules.serverpod_auth_idp,
|
||||
'serverpod_auth_core': modules.serverpod_auth_core,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,109 +0,0 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
|
||||
/// A greeting message which can be sent to or from the server.
|
||||
abstract class Greeting
|
||||
implements _isc.SerializableModel, _isc.ProtocolSerialization {
|
||||
Greeting._({
|
||||
required this.message,
|
||||
required this.author,
|
||||
required this.timestamp,
|
||||
});
|
||||
|
||||
factory Greeting({
|
||||
required String message,
|
||||
required String author,
|
||||
required DateTime timestamp,
|
||||
}) = _GreetingImpl;
|
||||
|
||||
factory Greeting.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return Greeting(
|
||||
message: jsonSerialization['message'] as String,
|
||||
author: jsonSerialization['author'] as String,
|
||||
timestamp: _isc.DateTimeJsonExtension.fromJson(
|
||||
jsonSerialization['timestamp'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// The greeting message.
|
||||
String message;
|
||||
|
||||
/// The author of the greeting message.
|
||||
String author;
|
||||
|
||||
/// The time when the message was created.
|
||||
DateTime timestamp;
|
||||
|
||||
/// Returns a shallow copy of this [Greeting]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
Greeting copyWith({
|
||||
String? message,
|
||||
String? author,
|
||||
DateTime? timestamp,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'Greeting',
|
||||
'message': message,
|
||||
'author': author,
|
||||
'timestamp': timestamp.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'Greeting',
|
||||
'message': message,
|
||||
'author': author,
|
||||
'timestamp': timestamp.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _GreetingImpl extends Greeting {
|
||||
_GreetingImpl({
|
||||
required String message,
|
||||
required String author,
|
||||
required DateTime timestamp,
|
||||
}) : super._(
|
||||
message: message,
|
||||
author: author,
|
||||
timestamp: timestamp,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [Greeting]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
Greeting copyWith({
|
||||
String? message,
|
||||
String? author,
|
||||
DateTime? timestamp,
|
||||
}) {
|
||||
return Greeting(
|
||||
message: message ?? this.message,
|
||||
author: author ?? this.author,
|
||||
timestamp: timestamp ?? this.timestamp,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,13 +11,12 @@
|
|||
// ignore_for_file: dead_code, unnecessary_type_check
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:halkyon_base_client/halkyon_base_client.dart' as _ihk4p5t7;
|
||||
import 'package:serverpod_auth_core_client/serverpod_auth_core_client.dart'
|
||||
as _iacc;
|
||||
import 'package:serverpod_auth_idp_client/serverpod_auth_idp_client.dart'
|
||||
as _iaic;
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
import 'greetings/greeting.dart' as _izw8z7ou;
|
||||
export 'greetings/greeting.dart';
|
||||
export 'client.dart';
|
||||
|
||||
class Protocol extends _isc.SerializationManager {
|
||||
|
|
@ -54,12 +53,9 @@ class Protocol extends _isc.SerializationManager {
|
|||
}
|
||||
}
|
||||
|
||||
if (t == _izw8z7ou.Greeting) {
|
||||
return _izw8z7ou.Greeting.fromJson(data) as T;
|
||||
}
|
||||
if (t == _isc.getType<_izw8z7ou.Greeting?>()) {
|
||||
return (data != null ? _izw8z7ou.Greeting.fromJson(data) : null) as T;
|
||||
}
|
||||
try {
|
||||
return _ihk4p5t7.Protocol().deserialize<T>(data, t);
|
||||
} on _isc.DeserializationTypeNotFoundException catch (_) {}
|
||||
try {
|
||||
return _iaic.Protocol().deserialize<T>(data, t);
|
||||
} on _isc.DeserializationTypeNotFoundException catch (_) {}
|
||||
|
|
@ -71,7 +67,6 @@ class Protocol extends _isc.SerializationManager {
|
|||
|
||||
static String? getClassNameForType(Type type) {
|
||||
return switch (type) {
|
||||
_izw8z7ou.Greeting => 'Greeting',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
|
@ -88,9 +83,9 @@ class Protocol extends _isc.SerializationManager {
|
|||
);
|
||||
}
|
||||
|
||||
switch (data) {
|
||||
case _izw8z7ou.Greeting():
|
||||
return 'Greeting';
|
||||
className = _ihk4p5t7.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
return className.contains('.') ? className : 'halkyon_base.$className';
|
||||
}
|
||||
className = _iaic.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
|
|
@ -113,8 +108,9 @@ class Protocol extends _isc.SerializationManager {
|
|||
if (dataClassName is! String) {
|
||||
return super.deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName == 'Greeting') {
|
||||
return deserialize<_izw8z7ou.Greeting>(data['data']);
|
||||
if (dataClassName.startsWith('halkyon_base.')) {
|
||||
data['className'] = dataClassName.substring(13);
|
||||
return _ihk4p5t7.Protocol().deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName.startsWith('serverpod_auth_idp.')) {
|
||||
data['className'] = dataClassName.substring(19);
|
||||
|
|
@ -128,6 +124,7 @@ class Protocol extends _isc.SerializationManager {
|
|||
}
|
||||
|
||||
void _registerHostProtocols() {
|
||||
_ihk4p5t7.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_iaic.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_iacc.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
}
|
||||
|
|
@ -144,6 +141,9 @@ class Protocol extends _isc.SerializationManager {
|
|||
if (record == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return _ihk4p5t7.Protocol().mapRecordToJson(record);
|
||||
} catch (_) {}
|
||||
try {
|
||||
return _iaic.Protocol().mapRecordToJson(record);
|
||||
} catch (_) {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
name: halkyon_todo_client
|
||||
description: Starting point for a Serverpod client.
|
||||
description: Halkyon Todo's client, generated by Serverpod.
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: '^3.13.0'
|
||||
|
|
@ -7,5 +8,8 @@ environment:
|
|||
resolution: workspace
|
||||
|
||||
dependencies:
|
||||
# The client side of halkyon_base (ADR 0014), as the server's.
|
||||
halkyon_base_client:
|
||||
path: ../../../packages/halkyon_base_client
|
||||
serverpod_auth_idp_client: 4.0.4
|
||||
serverpod_client: 4.0.4
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
# Build from the project root with:
|
||||
# docker build -f halkyon_todo_server/Dockerfile .
|
||||
|
||||
# Build stage
|
||||
FROM dart:3.12.2 AS build
|
||||
|
||||
# Create a minimal workspace that preserves locked server dependency versions.
|
||||
WORKDIR /app
|
||||
COPY pubspec.lock .
|
||||
COPY halkyon_todo_server halkyon_todo_server
|
||||
RUN printf '%s\n' \
|
||||
'name: _' \
|
||||
'environment:' \
|
||||
' sdk: ^3.12.2' \
|
||||
'workspace:' \
|
||||
' - halkyon_todo_server' \
|
||||
> pubspec.yaml
|
||||
RUN dart pub get
|
||||
|
||||
# Compile the server executable.
|
||||
WORKDIR /app/halkyon_todo_server
|
||||
RUN dart build cli --target bin/main.dart --output build
|
||||
RUN mv build/bundle/bin/main build/bundle/bin/server
|
||||
|
||||
# Add a fallback for the copy of possibly missing directories.
|
||||
RUN mkdir -p config web migrations
|
||||
|
||||
# Final stage
|
||||
FROM alpine:latest
|
||||
WORKDIR /app
|
||||
|
||||
# Environment variables
|
||||
ENV runmode=production
|
||||
ENV serverid=default
|
||||
ENV logging=normal
|
||||
ENV role=monolith
|
||||
|
||||
# Copy runtime dependencies
|
||||
COPY --from=build /runtime/ /
|
||||
|
||||
# Copy compiled server executable and native libraries
|
||||
COPY --from=build /app/halkyon_todo_server/build/bundle/ ./
|
||||
|
||||
# Copy configuration files and resources
|
||||
COPY --from=build /app/halkyon_todo_server/config/ config/
|
||||
COPY --from=build /app/halkyon_todo_server/web/ web/
|
||||
COPY --from=build /app/halkyon_todo_server/migrations/ migrations/
|
||||
|
||||
# This file is required to enable the endpoint log filter in Insights.
|
||||
COPY --from=build /app/halkyon_todo_server/lib/src/generated/protocol.yaml lib/src/generated/protocol.yaml
|
||||
|
||||
# Expose ports
|
||||
EXPOSE 8080
|
||||
EXPOSE 8081
|
||||
EXPOSE 8082
|
||||
|
||||
# Define the entrypoint command
|
||||
ENTRYPOINT ./bin/server --mode=$runmode --server-id=$serverid --logging=$logging --role=$role
|
||||
|
|
@ -1,34 +1,21 @@
|
|||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
linter:
|
||||
rules:
|
||||
unawaited_futures: true
|
||||
avoid_print: true
|
||||
# The workspace's lint rules (see ../../../analysis_options.yaml), minus generated code.
|
||||
include: ../../../analysis_options.yaml
|
||||
|
||||
analyzer:
|
||||
exclude:
|
||||
- lib/src/generated/**
|
||||
- test/integration/test_tools/serverpod_test_tools.dart
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
# Serverpod's code generator reads this package with an analyzer that doesn't know
|
||||
# Dart v3.13's primary constructors yet, and fails on them in endpoint files, so the
|
||||
# server keeps the classic constructor and class-body syntax, as Learn's does; switch
|
||||
# these back on once Serverpod parses it.
|
||||
linter:
|
||||
rules:
|
||||
empty_container_bodies: false
|
||||
unnecessary_type_name_in_constructor: false
|
||||
use_declaring_parameters: false
|
||||
use_primary_constructors: false
|
||||
|
||||
formatter:
|
||||
trailing_commas: preserve
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import 'package:halkyon_todo_server/server.dart';
|
||||
|
||||
/// This is the starting point for your Serverpod server. Typically, there is
|
||||
/// no need to modify this file.
|
||||
void main(List<String> args) {
|
||||
run(args);
|
||||
}
|
||||
/// Starts Halkyon Todo's server (lib/server.dart).
|
||||
Future<void> main(List<String> args) => run(args);
|
||||
|
|
|
|||
|
|
@ -41,8 +41,9 @@ database:
|
|||
|
||||
maxRequestSize: 524288 # The maximum size of requests allowed in bytes
|
||||
|
||||
# Session logs stay out of the database, for privacy, as Learn's (never re-enable).
|
||||
sessionLogs:
|
||||
persistentEnabled: true
|
||||
persistentEnabled: false
|
||||
consoleEnabled: true
|
||||
consoleLogFormat: text # Defaults to "json", options are "text" or "json"
|
||||
|
||||
|
|
|
|||
|
|
@ -2,3 +2,8 @@ type: server
|
|||
|
||||
client_package_path: ../halkyon_todo_client
|
||||
server_test_tools_path: test/integration/test_tools
|
||||
|
||||
modules:
|
||||
# What every Halkyon server has (ADR 0014): `client.modules.base` in the app.
|
||||
halkyon_base:
|
||||
nickname: base
|
||||
|
|
|
|||
|
|
@ -41,9 +41,10 @@ database:
|
|||
|
||||
maxRequestSize: 524288 # The maximum size of requests allowed in bytes
|
||||
|
||||
# Session logs stay out of the database, for privacy, as Learn's (never re-enable).
|
||||
sessionLogs:
|
||||
consoleEnabled: false
|
||||
# persistentEnabled: true
|
||||
persistentEnabled: false
|
||||
# consoleLogFormat: json # Defaults to "json", options are "text" or "json"
|
||||
|
||||
#futureCall:
|
||||
|
|
|
|||
|
|
@ -45,8 +45,9 @@ database:
|
|||
|
||||
maxRequestSize: 524288 # The maximum size of requests allowed in bytes
|
||||
|
||||
# Session logs stay out of the database, for privacy, as Learn's (never re-enable).
|
||||
sessionLogs:
|
||||
# persistentEnabled: true
|
||||
persistentEnabled: false
|
||||
# consoleEnabled: true
|
||||
# consoleLogFormat: json # Defaults to "json", options are "text" or "json"
|
||||
|
||||
|
|
|
|||
|
|
@ -38,8 +38,9 @@ database:
|
|||
#maxConnectionCount: 10 # Defaults to 10, a negative or null value removes the limit
|
||||
|
||||
|
||||
# Session logs stay out of the database, for privacy, as Learn's (never re-enable).
|
||||
sessionLogs:
|
||||
persistentEnabled: true
|
||||
persistentEnabled: false
|
||||
consoleEnabled: true
|
||||
# consoleLogFormat: json # Defaults to "json", options are "text" or "json"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
services:
|
||||
# Development services
|
||||
postgres:
|
||||
image: ghcr.io/serverpod/postgres:16
|
||||
ports:
|
||||
- "8090:5432"
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: halkyon_todo
|
||||
POSTGRES_PASSWORD: "aKpX2DK-n57tgkIr0XqP0hZSxmcwLQdj"
|
||||
volumes:
|
||||
- halkyon_todo_data:/var/lib/postgresql/data
|
||||
|
||||
|
||||
# Test services
|
||||
postgres_test:
|
||||
image: ghcr.io/serverpod/postgres:16
|
||||
ports:
|
||||
- "9090:5432"
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: halkyon_todo_test
|
||||
POSTGRES_PASSWORD: "ls3BpmIUIv2u_Ud0eZ2h3bunjU5nDzkh"
|
||||
volumes:
|
||||
- halkyon_todo_test_data:/var/lib/postgresql/data
|
||||
|
||||
|
||||
volumes:
|
||||
halkyon_todo_data:
|
||||
halkyon_todo_test_data:
|
||||
|
|
@ -1,109 +1,25 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:halkyon_base_server/base.dart';
|
||||
import 'package:halkyon_todo_server/src/generated/serverpod.dart';
|
||||
import 'package:halkyon_todo_server/src/pruning.dart';
|
||||
import 'package:halkyon_todo_server/src/todo_base.dart';
|
||||
import 'package:serverpod_auth_idp_server/core.dart';
|
||||
import 'package:serverpod_auth_idp_server/providers/email.dart';
|
||||
import 'package:serverpod_cloud_storage/serverpod_cloud_storage.dart';
|
||||
|
||||
import 'src/cache_busting.dart';
|
||||
import 'src/generated/serverpod.dart';
|
||||
import 'src/web/routes/app_config_route.dart';
|
||||
|
||||
/// The starting point of the Serverpod server.
|
||||
void run(List<String> args) async {
|
||||
// Initialize Serverpod. The generated Serverpod class is already connected
|
||||
// with your project's generated code.
|
||||
/// The starting point of Halkyon Todo's server.
|
||||
Future<void> run(List<String> args) async {
|
||||
// The generated Serverpod is already connected with the generated code.
|
||||
final pod = Serverpod(args);
|
||||
|
||||
// Initialize authentication services for the server.
|
||||
// Token managers will be used to validate and issue authentication keys,
|
||||
// and the identity providers will be the authentication options available for users.
|
||||
// What every Halkyon server has (ADR 0014): sign-in, the account and its
|
||||
// deletion, the pruning, the instance's basics, set up for Todo.
|
||||
HalkyonBase.configure(pod, todoBase());
|
||||
// Token managers validate and issue authentication keys; identity
|
||||
// providers are the ways people can sign in (halkyon_base's
|
||||
// auth_config.dart).
|
||||
pod.initializeAuthServices(
|
||||
tokenManagerBuilders: [
|
||||
// Server-side sessions: every request looks its session up in the
|
||||
// database, so signing out or deleting an account takes effect at once.
|
||||
// A session unused for 90 days expires; one in use never does.
|
||||
ServerSideSessionsConfigFromPasswords(
|
||||
defaultSessionInactivityTimeout: const Duration(days: 90),
|
||||
),
|
||||
],
|
||||
identityProviderBuilders: [
|
||||
// Configure the email identity provider for email/password authentication.
|
||||
// The default setup works with Serverpod Cloud without configuration. In
|
||||
// development the verification codes are logged to the console, and in
|
||||
// staging and production they are sent through the Serverpod Cloud email
|
||||
// service. If you want to use a custom provider for sending emails, use
|
||||
// `EmailIdpConfigFromPasswords`.
|
||||
ServerpodCloudEmailIdpConfig(
|
||||
appDisplayName: 'halkyon_todo',
|
||||
),
|
||||
],
|
||||
tokenManagerBuilders: tokenManagerBuilders,
|
||||
identityProviderBuilders: identityProviderBuilders(pod),
|
||||
);
|
||||
|
||||
// Serve all files in the web/static relative directory under /web.
|
||||
// These are used by the default web page.
|
||||
pod.webServer.addRoute(
|
||||
StaticRoute.withCacheBusting(cacheBustingConfig),
|
||||
cacheBustingConfig.mountPrefix,
|
||||
);
|
||||
|
||||
// Setup the app config route.
|
||||
// We build this configuration based on the servers api url and serve it to
|
||||
// the flutter app.
|
||||
pod.webServer.addRoute(
|
||||
AppConfigRoute(apiConfig: pod.config.apiServer),
|
||||
'/assets/assets/config.json',
|
||||
);
|
||||
|
||||
// Checks if the flutter web app has been built and serves it if it has.
|
||||
final appDir = Directory(Uri(path: 'web/app').toFilePath());
|
||||
if (appDir.existsSync()) {
|
||||
// Serve the flutter web app under /.
|
||||
pod.webServer.addRoute(
|
||||
FlutterRoute(
|
||||
appDir,
|
||||
// If building the Flutter app with WASM, set the below parameter to
|
||||
// true and add the --wasm flag to the flutter build command.
|
||||
enableWasmHeaders: false,
|
||||
),
|
||||
'/',
|
||||
);
|
||||
} else {
|
||||
// If the flutter web app has not been built, serve the build app page.
|
||||
final defaultRoute = StaticRoute.file(
|
||||
File(
|
||||
Uri(path: 'web/pages/build_flutter_app.html').toFilePath(),
|
||||
),
|
||||
);
|
||||
|
||||
pod.webServer.addMiddleware(
|
||||
FallbackMiddleware(
|
||||
fallback: defaultRoute,
|
||||
on: (response) => response.statusCode == 404,
|
||||
).call,
|
||||
'/',
|
||||
);
|
||||
|
||||
pod.webServer.addRoute(
|
||||
defaultRoute,
|
||||
'/**',
|
||||
);
|
||||
}
|
||||
|
||||
// Configure cloud storage.
|
||||
// This setup works with Serverpod Cloud without extra configuration.
|
||||
// If you want to use a custom provider for cloud storage, replace these
|
||||
// with your preferred provider.
|
||||
pod.addCloudStorage(
|
||||
await ServerpodCloudProvider.private(
|
||||
fallback: () => DatabaseCloudStorage('private'),
|
||||
),
|
||||
);
|
||||
pod.addCloudStorage(
|
||||
await ServerpodCloudProvider.public(
|
||||
fallback: () => DatabaseCloudStorage('public'),
|
||||
),
|
||||
);
|
||||
|
||||
// Start the server.
|
||||
await pod.start();
|
||||
|
||||
await schedulePruning(pod);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
import 'package:serverpod_auth_idp_server/providers/email.dart';
|
||||
|
||||
/// By extending [EmailIdpBaseEndpoint], the email identity provider endpoints
|
||||
/// are made available on the server and enable the corresponding sign-in widget
|
||||
/// on the client.
|
||||
class EmailIdpEndpoint extends EmailIdpBaseEndpoint {}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:serverpod/serverpod.dart';
|
||||
|
||||
/// Cache busting configuration.
|
||||
/// Used in WidgetRoutes to cache-bust asset paths.
|
||||
final cacheBustingConfig = CacheBustingConfig(
|
||||
mountPrefix: '/web',
|
||||
fileSystemRoot: Directory(Uri(path: 'web/static').toFilePath()),
|
||||
);
|
||||
|
|
@ -10,239 +10,29 @@
|
|||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:halkyon_base_server/halkyon_base_server.dart' as _ik8molhd;
|
||||
import 'package:halkyon_todo_server/src/generated/future_calls.dart'
|
||||
as _i772x17y;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
import 'package:serverpod_auth_core_server/serverpod_auth_core_server.dart'
|
||||
as _iacs;
|
||||
import 'package:serverpod_auth_idp_server/serverpod_auth_idp_server.dart'
|
||||
as _iais;
|
||||
import '../auth/email_idp_endpoint.dart' as _iuc1hd5t;
|
||||
import '../greetings/greeting_endpoint.dart' as _il624ik7;
|
||||
export 'future_calls.dart' show ServerpodFutureCallsGetter;
|
||||
|
||||
class Endpoints extends _is.EndpointDispatch {
|
||||
@override
|
||||
void initializeEndpoints(_is.Server server) {
|
||||
var endpoints = <String, _is.Endpoint>{
|
||||
'emailIdp': _iuc1hd5t.EmailIdpEndpoint()
|
||||
..initialize(
|
||||
server,
|
||||
'emailIdp',
|
||||
null,
|
||||
),
|
||||
'greeting': _il624ik7.GreetingEndpoint()
|
||||
..initialize(
|
||||
server,
|
||||
'greeting',
|
||||
null,
|
||||
),
|
||||
};
|
||||
connectors['emailIdp'] = _is.EndpointConnector(
|
||||
name: 'emailIdp',
|
||||
endpoint: endpoints['emailIdp']!,
|
||||
methodConnectors: {
|
||||
'login': _is.MethodConnector(
|
||||
name: 'login',
|
||||
params: {
|
||||
'email': _is.ParameterDescription(
|
||||
name: 'email',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
'password': _is.ParameterDescription(
|
||||
name: 'password',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async =>
|
||||
(endpoints['emailIdp'] as _iuc1hd5t.EmailIdpEndpoint).login(
|
||||
session,
|
||||
email: params['email'],
|
||||
password: params['password'],
|
||||
),
|
||||
),
|
||||
'startRegistration': _is.MethodConnector(
|
||||
name: 'startRegistration',
|
||||
params: {
|
||||
'email': _is.ParameterDescription(
|
||||
name: 'email',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async => (endpoints['emailIdp'] as _iuc1hd5t.EmailIdpEndpoint)
|
||||
.startRegistration(
|
||||
session,
|
||||
email: params['email'],
|
||||
),
|
||||
),
|
||||
'verifyRegistrationCode': _is.MethodConnector(
|
||||
name: 'verifyRegistrationCode',
|
||||
params: {
|
||||
'accountRequestId': _is.ParameterDescription(
|
||||
name: 'accountRequestId',
|
||||
type: _is.getType<_is.UuidValue>(),
|
||||
nullable: false,
|
||||
),
|
||||
'verificationCode': _is.ParameterDescription(
|
||||
name: 'verificationCode',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async => (endpoints['emailIdp'] as _iuc1hd5t.EmailIdpEndpoint)
|
||||
.verifyRegistrationCode(
|
||||
session,
|
||||
accountRequestId: params['accountRequestId'],
|
||||
verificationCode: params['verificationCode'],
|
||||
),
|
||||
),
|
||||
'finishRegistration': _is.MethodConnector(
|
||||
name: 'finishRegistration',
|
||||
params: {
|
||||
'registrationToken': _is.ParameterDescription(
|
||||
name: 'registrationToken',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
'password': _is.ParameterDescription(
|
||||
name: 'password',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async => (endpoints['emailIdp'] as _iuc1hd5t.EmailIdpEndpoint)
|
||||
.finishRegistration(
|
||||
session,
|
||||
registrationToken: params['registrationToken'],
|
||||
password: params['password'],
|
||||
),
|
||||
),
|
||||
'startPasswordReset': _is.MethodConnector(
|
||||
name: 'startPasswordReset',
|
||||
params: {
|
||||
'email': _is.ParameterDescription(
|
||||
name: 'email',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async => (endpoints['emailIdp'] as _iuc1hd5t.EmailIdpEndpoint)
|
||||
.startPasswordReset(
|
||||
session,
|
||||
email: params['email'],
|
||||
),
|
||||
),
|
||||
'verifyPasswordResetCode': _is.MethodConnector(
|
||||
name: 'verifyPasswordResetCode',
|
||||
params: {
|
||||
'passwordResetRequestId': _is.ParameterDescription(
|
||||
name: 'passwordResetRequestId',
|
||||
type: _is.getType<_is.UuidValue>(),
|
||||
nullable: false,
|
||||
),
|
||||
'verificationCode': _is.ParameterDescription(
|
||||
name: 'verificationCode',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async => (endpoints['emailIdp'] as _iuc1hd5t.EmailIdpEndpoint)
|
||||
.verifyPasswordResetCode(
|
||||
session,
|
||||
passwordResetRequestId: params['passwordResetRequestId'],
|
||||
verificationCode: params['verificationCode'],
|
||||
),
|
||||
),
|
||||
'finishPasswordReset': _is.MethodConnector(
|
||||
name: 'finishPasswordReset',
|
||||
params: {
|
||||
'finishPasswordResetToken': _is.ParameterDescription(
|
||||
name: 'finishPasswordResetToken',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
'newPassword': _is.ParameterDescription(
|
||||
name: 'newPassword',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async => (endpoints['emailIdp'] as _iuc1hd5t.EmailIdpEndpoint)
|
||||
.finishPasswordReset(
|
||||
session,
|
||||
finishPasswordResetToken:
|
||||
params['finishPasswordResetToken'],
|
||||
newPassword: params['newPassword'],
|
||||
),
|
||||
),
|
||||
'hasAccount': _is.MethodConnector(
|
||||
name: 'hasAccount',
|
||||
params: {},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async => (endpoints['emailIdp'] as _iuc1hd5t.EmailIdpEndpoint)
|
||||
.hasAccount(session),
|
||||
),
|
||||
},
|
||||
);
|
||||
connectors['greeting'] = _is.EndpointConnector(
|
||||
name: 'greeting',
|
||||
endpoint: endpoints['greeting']!,
|
||||
methodConnectors: {
|
||||
'hello': _is.MethodConnector(
|
||||
name: 'hello',
|
||||
params: {
|
||||
'name': _is.ParameterDescription(
|
||||
name: 'name',
|
||||
type: _is.getType<String>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
call:
|
||||
(
|
||||
_is.Session session,
|
||||
Map<String, dynamic> params,
|
||||
) async =>
|
||||
(endpoints['greeting'] as _il624ik7.GreetingEndpoint).hello(
|
||||
session,
|
||||
params['name'],
|
||||
),
|
||||
),
|
||||
},
|
||||
);
|
||||
modules['halkyon_base'] = _ik8molhd.Endpoints()
|
||||
..initializeEndpoints(server);
|
||||
modules['serverpod_auth_idp'] = _iais.Endpoints()
|
||||
..initializeEndpoints(server);
|
||||
modules['serverpod_auth_core'] = _iacs.Endpoints()
|
||||
..initializeEndpoints(server);
|
||||
}
|
||||
|
||||
@override
|
||||
_is.FutureCallDispatch? get futureCalls {
|
||||
return _i772x17y.FutureCalls();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,232 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
// ignore_for_file: depend_on_referenced_packages
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:async' as _ida;
|
||||
import 'package:clock/clock.dart' as _io0w16m8;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
import '../pruning_future_call.dart' as _ibrplims;
|
||||
|
||||
/// Invokes a future call.
|
||||
typedef _InvokeFutureCall =
|
||||
Future<void> Function(String name, _is.SerializableModel? object);
|
||||
|
||||
extension ServerpodFutureCallsGetter on _is.Serverpod {
|
||||
/// Generated future calls.
|
||||
FutureCalls get futureCalls => FutureCalls();
|
||||
}
|
||||
|
||||
class FutureCalls extends _is.FutureCallDispatch<_FutureCallRef> {
|
||||
FutureCalls._();
|
||||
|
||||
factory FutureCalls() {
|
||||
return _instance;
|
||||
}
|
||||
|
||||
static final FutureCalls _instance = FutureCalls._();
|
||||
|
||||
_is.FutureCallManager? _futureCallManager;
|
||||
|
||||
String? _serverId;
|
||||
|
||||
String get _effectiveServerId {
|
||||
if (_serverId == null) {
|
||||
throw StateError('FutureCalls is not initialized.');
|
||||
}
|
||||
return _serverId!;
|
||||
}
|
||||
|
||||
_is.FutureCallManager get _effectiveFutureCallManager {
|
||||
if (_futureCallManager == null) {
|
||||
throw StateError('FutureCalls is not initialized.');
|
||||
}
|
||||
return _futureCallManager!;
|
||||
}
|
||||
|
||||
@override
|
||||
void initialize(
|
||||
_is.FutureCallManager futureCallManager,
|
||||
String serverId,
|
||||
) {
|
||||
var registeredFutureCalls = <String, _is.InvokableFutureCall>{
|
||||
'PruningPruneAnonymousPeopleFutureCall':
|
||||
PruningPruneAnonymousPeopleFutureCall(),
|
||||
'PruningForgetSignUpAttemptsFutureCall':
|
||||
PruningForgetSignUpAttemptsFutureCall(),
|
||||
};
|
||||
_futureCallManager = futureCallManager;
|
||||
_serverId = serverId;
|
||||
for (final entry in registeredFutureCalls.entries) {
|
||||
_futureCallManager?.registerFutureCall(entry.value, entry.key);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
_FutureCallRef callAtTime(
|
||||
DateTime time, {
|
||||
String? identifier,
|
||||
}) {
|
||||
return _FutureCallRef(
|
||||
(name, object) {
|
||||
return _effectiveFutureCallManager.scheduleFutureCall(
|
||||
name,
|
||||
object,
|
||||
time,
|
||||
_effectiveServerId,
|
||||
identifier,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
_FutureCallRef callWithDelay(
|
||||
Duration delay, {
|
||||
String? identifier,
|
||||
}) {
|
||||
return _FutureCallRef(
|
||||
(name, object) {
|
||||
return _effectiveFutureCallManager.scheduleFutureCall(
|
||||
name,
|
||||
object,
|
||||
DateTime.now().toUtc().add(delay),
|
||||
_effectiveServerId,
|
||||
identifier,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
_is.RecurringFutureCallDispatch<_FutureCallRef> callRecurring({
|
||||
String? identifier,
|
||||
}) {
|
||||
return _RecurringFutureCallDispatchImpl(
|
||||
_effectiveFutureCallManager,
|
||||
_effectiveServerId,
|
||||
identifier,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> cancel(String identifier) async {
|
||||
await _effectiveFutureCallManager.cancelFutureCall(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
class _RecurringFutureCallDispatchImpl
|
||||
extends _is.RecurringFutureCallDispatch<_FutureCallRef> {
|
||||
_RecurringFutureCallDispatchImpl(
|
||||
this._futureCallManager,
|
||||
this._serverId,
|
||||
this._identifier,
|
||||
);
|
||||
|
||||
final _is.FutureCallManager _futureCallManager;
|
||||
|
||||
final String _serverId;
|
||||
|
||||
final String? _identifier;
|
||||
|
||||
@override
|
||||
_FutureCallRef cron(String cronExpression) {
|
||||
return _FutureCallRef(
|
||||
(name, object) {
|
||||
return _futureCallManager.scheduleFutureCall(
|
||||
name,
|
||||
object,
|
||||
_is.Cron.parse(cronExpression).nextTime(),
|
||||
_serverId,
|
||||
_identifier,
|
||||
scheduling: _is.CronFutureCallScheduling(cron: cronExpression),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
_FutureCallRef every(
|
||||
Duration interval, {
|
||||
DateTime? start,
|
||||
}) {
|
||||
final now = _io0w16m8.clock.now().toUtc();
|
||||
return _FutureCallRef(
|
||||
(name, object) {
|
||||
return _futureCallManager.scheduleFutureCall(
|
||||
name,
|
||||
object,
|
||||
start ?? now.add(interval),
|
||||
_serverId,
|
||||
_identifier,
|
||||
scheduling: _is.IntervalFutureCallScheduling(
|
||||
interval: interval,
|
||||
start: start,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FutureCallRef {
|
||||
_FutureCallRef(this._invokeFutureCall);
|
||||
|
||||
final _InvokeFutureCall _invokeFutureCall;
|
||||
|
||||
late final pruning = _PruningFutureCallDispatcher(_invokeFutureCall);
|
||||
}
|
||||
|
||||
class _PruningFutureCallDispatcher {
|
||||
_PruningFutureCallDispatcher(this._invokeFutureCall);
|
||||
|
||||
final _InvokeFutureCall _invokeFutureCall;
|
||||
|
||||
Future<void> pruneAnonymousPeople() {
|
||||
return _invokeFutureCall(
|
||||
'PruningPruneAnonymousPeopleFutureCall',
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> forgetSignUpAttempts() {
|
||||
return _invokeFutureCall(
|
||||
'PruningForgetSignUpAttemptsFutureCall',
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Prunes one batch and logs how many people went.
|
||||
class PruningPruneAnonymousPeopleFutureCall extends _is.FutureCall
|
||||
implements _is.InvokableFutureCall {
|
||||
@override
|
||||
_ida.Future<void> invoke(
|
||||
_is.Session session,
|
||||
_is.SerializableModel? object,
|
||||
) async {
|
||||
await _ibrplims.PruningFutureCall().pruneAnonymousPeople(session);
|
||||
}
|
||||
}
|
||||
|
||||
/// Forgets the sign-up attempts the rate limit is done with, with their IP
|
||||
/// addresses, and the email sign-in codes past their time, and logs how
|
||||
/// many.
|
||||
class PruningForgetSignUpAttemptsFutureCall extends _is.FutureCall
|
||||
implements _is.InvokableFutureCall {
|
||||
@override
|
||||
_ida.Future<void> invoke(
|
||||
_is.Session session,
|
||||
_is.SerializableModel? object,
|
||||
) async {
|
||||
await _ibrplims.PruningFutureCall().forgetSignUpAttempts(session);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// A greeting message which can be sent to or from the server.
|
||||
abstract class Greeting
|
||||
implements _is.SerializableModel, _is.ProtocolSerialization {
|
||||
Greeting._({
|
||||
required this.message,
|
||||
required this.author,
|
||||
required this.timestamp,
|
||||
});
|
||||
|
||||
factory Greeting({
|
||||
required String message,
|
||||
required String author,
|
||||
required DateTime timestamp,
|
||||
}) = _GreetingImpl;
|
||||
|
||||
factory Greeting.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return Greeting(
|
||||
message: jsonSerialization['message'] as String,
|
||||
author: jsonSerialization['author'] as String,
|
||||
timestamp: _is.DateTimeJsonExtension.fromJson(
|
||||
jsonSerialization['timestamp'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// The greeting message.
|
||||
String message;
|
||||
|
||||
/// The author of the greeting message.
|
||||
String author;
|
||||
|
||||
/// The time when the message was created.
|
||||
DateTime timestamp;
|
||||
|
||||
/// Returns a shallow copy of this [Greeting]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
Greeting copyWith({
|
||||
String? message,
|
||||
String? author,
|
||||
DateTime? timestamp,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'Greeting',
|
||||
'message': message,
|
||||
'author': author,
|
||||
'timestamp': timestamp.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'Greeting',
|
||||
'message': message,
|
||||
'author': author,
|
||||
'timestamp': timestamp.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _GreetingImpl extends Greeting {
|
||||
_GreetingImpl({
|
||||
required String message,
|
||||
required String author,
|
||||
required DateTime timestamp,
|
||||
}) : super._(
|
||||
message: message,
|
||||
author: author,
|
||||
timestamp: timestamp,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [Greeting]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
Greeting copyWith({
|
||||
String? message,
|
||||
String? author,
|
||||
DateTime? timestamp,
|
||||
}) {
|
||||
return Greeting(
|
||||
message: message ?? this.message,
|
||||
author: author ?? this.author,
|
||||
timestamp: timestamp ?? this.timestamp,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,14 +11,13 @@
|
|||
// ignore_for_file: dead_code, unnecessary_type_check
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:halkyon_base_server/halkyon_base_server.dart' as _ik8molhd;
|
||||
import 'package:serverpod/protocol.dart' as _isp;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
import 'package:serverpod_auth_core_server/serverpod_auth_core_server.dart'
|
||||
as _iacs;
|
||||
import 'package:serverpod_auth_idp_server/serverpod_auth_idp_server.dart'
|
||||
as _iais;
|
||||
import 'greetings/greeting.dart' as _izw8z7ou;
|
||||
export 'greetings/greeting.dart';
|
||||
|
||||
class Protocol extends _is.DatabaseSerializationManager {
|
||||
Protocol._();
|
||||
|
|
@ -28,6 +27,7 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
static final Protocol _instance = Protocol._().._registerHostProtocols();
|
||||
|
||||
static List<_isp.TableDefinition> get targetTableDefinitions => [
|
||||
..._ik8molhd.Protocol.targetTableDefinitions,
|
||||
..._iais.Protocol.targetTableDefinitions,
|
||||
..._iacs.Protocol.targetTableDefinitions,
|
||||
..._isp.Protocol.targetTableDefinitions,
|
||||
|
|
@ -60,12 +60,9 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
}
|
||||
}
|
||||
|
||||
if (t == _izw8z7ou.Greeting) {
|
||||
return _izw8z7ou.Greeting.fromJson(data) as T;
|
||||
}
|
||||
if (t == _is.getType<_izw8z7ou.Greeting?>()) {
|
||||
return (data != null ? _izw8z7ou.Greeting.fromJson(data) : null) as T;
|
||||
}
|
||||
try {
|
||||
return _ik8molhd.Protocol().deserialize<T>(data, t);
|
||||
} on _is.DeserializationTypeNotFoundException catch (_) {}
|
||||
try {
|
||||
return _iais.Protocol().deserialize<T>(data, t);
|
||||
} on _is.DeserializationTypeNotFoundException catch (_) {}
|
||||
|
|
@ -80,7 +77,6 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
|
||||
static String? getClassNameForType(Type type) {
|
||||
return switch (type) {
|
||||
_izw8z7ou.Greeting => 'Greeting',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
|
@ -97,9 +93,9 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
);
|
||||
}
|
||||
|
||||
switch (data) {
|
||||
case _izw8z7ou.Greeting():
|
||||
return 'Greeting';
|
||||
className = _ik8molhd.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
return className.contains('.') ? className : 'halkyon_base.$className';
|
||||
}
|
||||
className = _iais.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
|
|
@ -126,8 +122,9 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (dataClassName is! String) {
|
||||
return super.deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName == 'Greeting') {
|
||||
return deserialize<_izw8z7ou.Greeting>(data['data']);
|
||||
if (dataClassName.startsWith('halkyon_base.')) {
|
||||
data['className'] = dataClassName.substring(13);
|
||||
return _ik8molhd.Protocol().deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName.startsWith('serverpod_auth_idp.')) {
|
||||
data['className'] = dataClassName.substring(19);
|
||||
|
|
@ -145,12 +142,19 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
}
|
||||
|
||||
void _registerHostProtocols() {
|
||||
_ik8molhd.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_iais.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_iacs.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
}
|
||||
|
||||
@override
|
||||
_is.Table? getTableForType(Type t) {
|
||||
{
|
||||
var table = _ik8molhd.Protocol().getTableForType(t);
|
||||
if (table != null) {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
{
|
||||
var table = _iais.Protocol().getTableForType(t);
|
||||
if (table != null) {
|
||||
|
|
@ -188,6 +192,9 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (record == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return _ik8molhd.Protocol().mapRecordToJson(record);
|
||||
} catch (_) {}
|
||||
try {
|
||||
return _iais.Protocol().mapRecordToJson(record);
|
||||
} catch (_) {}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue