halkyon_app: instances, sign-in and sync plumbing for Halkyon's apps
What every Halkyon app's shell needs, taken from Halkyon Learn's and set on halkyon_base's client: the instances a device keeps an account on, the first start's tiles and where an instance keeps people's data, the account switcher; signing in anonymously, with GitHub or an email code, switching person and the start screen; the gates that build an app's dependencies per instance and per person; and the sync service with its status, Settings card and banner. Texts of its own in English and German take the app's name; what only the app can say, it passes in. Fakes for apps' tests are in testing.dart. An instance's basics now name the app it serves (`app`, set by the host's configuration), and an app accepts only an instance of its own. SerialQueue and CoalescingRunner move into halkyon_core. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
af1de799f4
commit
e23cc77f7c
4 changed files with 10761 additions and 10 deletions
|
|
@ -13,12 +13,13 @@
|
|||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// What every Halkyon instance tells its apps, to anyone, before anyone signs
|
||||
/// in: how people sign in here, where the source of what it runs is, the
|
||||
/// in: which app it serves, how people sign in here, where the source of what it runs is, the
|
||||
/// oldest app build it works with, and where it keeps people's data. Every
|
||||
/// instance is run by someone else, with its own configuration.
|
||||
abstract class InstanceBasics
|
||||
implements _is.SerializableModel, _is.ProtocolSerialization {
|
||||
InstanceBasics._({
|
||||
required this.app,
|
||||
this.gitHubClientId,
|
||||
required this.emailSignIn,
|
||||
required this.sourceCodeUrl,
|
||||
|
|
@ -28,6 +29,7 @@ abstract class InstanceBasics
|
|||
});
|
||||
|
||||
factory InstanceBasics({
|
||||
required String app,
|
||||
String? gitHubClientId,
|
||||
required bool emailSignIn,
|
||||
required String sourceCodeUrl,
|
||||
|
|
@ -38,6 +40,7 @@ abstract class InstanceBasics
|
|||
|
||||
factory InstanceBasics.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return InstanceBasics(
|
||||
app: jsonSerialization['app'] as String,
|
||||
gitHubClientId: jsonSerialization['gitHubClientId'] as String?,
|
||||
emailSignIn: _is.BoolJsonExtension.fromJson(
|
||||
jsonSerialization['emailSignIn'],
|
||||
|
|
@ -49,6 +52,10 @@ abstract class InstanceBasics
|
|||
);
|
||||
}
|
||||
|
||||
/// Which app this instance serves (`learn` for Halkyon Learn), as its host
|
||||
/// configures it: an app accepts only an instance of its own.
|
||||
String app;
|
||||
|
||||
/// The client ID of this instance's GitHub OAuth app, or null when GitHub
|
||||
/// sign-in is off here.
|
||||
String? gitHubClientId;
|
||||
|
|
@ -79,6 +86,7 @@ abstract class InstanceBasics
|
|||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
InstanceBasics copyWith({
|
||||
String? app,
|
||||
String? gitHubClientId,
|
||||
bool? emailSignIn,
|
||||
String? sourceCodeUrl,
|
||||
|
|
@ -90,6 +98,7 @@ abstract class InstanceBasics
|
|||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.InstanceBasics',
|
||||
'app': app,
|
||||
if (gitHubClientId != null) 'gitHubClientId': gitHubClientId,
|
||||
'emailSignIn': emailSignIn,
|
||||
'sourceCodeUrl': sourceCodeUrl,
|
||||
|
|
@ -103,6 +112,7 @@ abstract class InstanceBasics
|
|||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.InstanceBasics',
|
||||
'app': app,
|
||||
if (gitHubClientId != null) 'gitHubClientId': gitHubClientId,
|
||||
'emailSignIn': emailSignIn,
|
||||
'sourceCodeUrl': sourceCodeUrl,
|
||||
|
|
@ -122,6 +132,7 @@ class _Undefined {}
|
|||
|
||||
class _InstanceBasicsImpl extends InstanceBasics {
|
||||
_InstanceBasicsImpl({
|
||||
required String app,
|
||||
String? gitHubClientId,
|
||||
required bool emailSignIn,
|
||||
required String sourceCodeUrl,
|
||||
|
|
@ -129,6 +140,7 @@ class _InstanceBasicsImpl extends InstanceBasics {
|
|||
String? dataLocation,
|
||||
String? backupLocation,
|
||||
}) : super._(
|
||||
app: app,
|
||||
gitHubClientId: gitHubClientId,
|
||||
emailSignIn: emailSignIn,
|
||||
sourceCodeUrl: sourceCodeUrl,
|
||||
|
|
@ -142,6 +154,7 @@ class _InstanceBasicsImpl extends InstanceBasics {
|
|||
@_is.useResult
|
||||
@override
|
||||
InstanceBasics copyWith({
|
||||
String? app,
|
||||
Object? gitHubClientId = _Undefined,
|
||||
bool? emailSignIn,
|
||||
String? sourceCodeUrl,
|
||||
|
|
@ -150,6 +163,7 @@ class _InstanceBasicsImpl extends InstanceBasics {
|
|||
Object? backupLocation = _Undefined,
|
||||
}) {
|
||||
return InstanceBasics(
|
||||
app: app ?? this.app,
|
||||
gitHubClientId: gitHubClientId is String?
|
||||
? gitHubClientId
|
||||
: this.gitHubClientId,
|
||||
|
|
|
|||
|
|
@ -20,12 +20,18 @@ typedef AfterDeletion = Future<void> Function();
|
|||
/// pod, before the pod starts ([configure]).
|
||||
final class HalkyonBase {
|
||||
HalkyonBase({
|
||||
required this.app,
|
||||
required this.appName,
|
||||
required this.upstreamSourceCodeUrl,
|
||||
required this.minimumAppBuild,
|
||||
this.deletePersonData = const [],
|
||||
});
|
||||
|
||||
/// Which app this server serves, as its apps know it (`learn` for Halkyon
|
||||
/// Learn): an instance names it in its basics, so an app recognises its own
|
||||
/// instances and refuses another app's. Never change it once apps use it.
|
||||
final String app;
|
||||
|
||||
/// The app's name as people know it, such as "Halkyon Learn": sign-in
|
||||
/// mails name it in their subject and text, and as their sender.
|
||||
final String appName;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
### What every Halkyon instance tells its apps, to anyone, before anyone signs
|
||||
### in: how people sign in here, where the source of what it runs is, the
|
||||
### in: which app it serves, how people sign in here, where the source of what it runs is, the
|
||||
### oldest app build it works with, and where it keeps people's data. Every
|
||||
### instance is run by someone else, with its own configuration.
|
||||
class: InstanceBasics
|
||||
fields:
|
||||
### Which app this instance serves (`learn` for Halkyon Learn), as its host
|
||||
### configures it: an app accepts only an instance of its own.
|
||||
app: String
|
||||
### The client ID of this instance's GitHub OAuth app, or null when GitHub
|
||||
### sign-in is off here.
|
||||
gitHubClientId: String?
|
||||
|
|
|
|||
|
|
@ -9,19 +9,21 @@ import 'package:serverpod/serverpod.dart';
|
|||
/// What every Halkyon instance tells its apps. Public: an app asks before
|
||||
/// anyone is signed in.
|
||||
class InstanceEndpoint extends Endpoint {
|
||||
/// This instance's basics: GitHub sign-in only with the operator's OAuth
|
||||
/// This instance's basics: the app it serves, GitHub sign-in only with the operator's OAuth
|
||||
/// app, email sign-in only where it can mail codes, where its source is,
|
||||
/// the oldest app build it works with, and where it keeps people's data
|
||||
/// and its backups.
|
||||
Future<InstanceBasics> basics(Session session) async {
|
||||
final pod = session.serverpod;
|
||||
final base = HalkyonBase.of(pod);
|
||||
return InstanceBasics(
|
||||
app: base.app,
|
||||
gitHubClientId: gitHubSignInAvailable(pod)
|
||||
? pod.getPassword('githubClientId')
|
||||
: null,
|
||||
emailSignIn: emailSignInAvailable(pod),
|
||||
sourceCodeUrl: sourceCodeUrl(pod),
|
||||
minimumAppBuild: HalkyonBase.of(pod).minimumAppBuild,
|
||||
minimumAppBuild: base.minimumAppBuild,
|
||||
dataLocation: dataLocation(),
|
||||
backupLocation: backupLocation(),
|
||||
);
|
||||
|
|
|
|||
Reference in a new issue