feat(meditate): Meditate's server, its sealed tables, and SimpleLogin's way back to it
halkyon_meditate_server and its generated client, copied from Todo's and stripped to the module, the register and the sync engine (ADR 0038, D1, D7): content_key, meditate_settings and sitting, synced and sealed, each indexing spaceId and deleted with the person's space; two migrations, the initial one and sittings (purely additive, the ADR explains the SQL); ports 8280 to 8282 and PostgreSQL 8290 (D13). Tests: the module's basics naming `meditate`, the sync engine refusing calls without a session, the register's modes, and a person's deletion taking their rows alone. halkyon_base's forwarding page knows Meditate's ways back (meditate-loopback on 43826, meditate-scheme app.halkyon.meditate), which halkyon_app's repository names among the apps' ports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
504aca3e68
commit
235d9cf75b
17 changed files with 47595 additions and 1 deletions
33
apps/meditate/halkyon_meditate_server/lib/server.dart
Normal file
33
apps/meditate/halkyon_meditate_server/lib/server.dart
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import 'package:halkyon_base_server/base.dart';
|
||||
import 'package:halkyon_meditate_server/src/generated/serverpod.dart';
|
||||
import 'package:halkyon_meditate_server/src/meditate_base.dart';
|
||||
import 'package:halkyon_meditate_server/src/pruning.dart';
|
||||
import 'package:serverpod_auth_idp_server/core.dart';
|
||||
|
||||
/// The starting point of Halkyon Meditate's server.
|
||||
Future<void> run(List<String> args) async {
|
||||
// The generated Serverpod is already connected with the generated code.
|
||||
final pod = Serverpod(args);
|
||||
// What every Halkyon server has (ADR 0014): sign-in, the account and its
|
||||
// deletion, the pruning, the instance's basics, set up for Meditate; a member
|
||||
// of the instance's register where the operator names one (ADR 0016).
|
||||
HalkyonBase.configure(
|
||||
pod,
|
||||
meditateBase(accounts: AccountsMode.fromEnvironment()),
|
||||
);
|
||||
// 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: tokenManagerBuilders,
|
||||
identityProviderBuilders: identityProviderBuilders(pod),
|
||||
);
|
||||
|
||||
// The calls between the instance's services, on the web server
|
||||
// (ADR 0016); Caddy never forwards them.
|
||||
addInstanceRoutes(pod);
|
||||
|
||||
await pod.start();
|
||||
|
||||
await schedulePruning(pod);
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/* 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:halkyon_base_server/halkyon_base_server.dart' as _ik8molhd;
|
||||
import 'package:halkyon_meditate_server/src/generated/future_calls.dart'
|
||||
as _iluznlp5;
|
||||
import 'package:halkyon_oidc_server/halkyon_oidc_server.dart' as _ipoqfiqr;
|
||||
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 'package:serverpod_offline_sync_server/serverpod_offline_sync_server.dart'
|
||||
as _izehhkf5;
|
||||
export 'future_calls.dart' show ServerpodFutureCallsGetter;
|
||||
|
||||
class Endpoints extends _is.EndpointDispatch {
|
||||
@override
|
||||
void initializeEndpoints(_is.Server server) {
|
||||
modules['halkyon_base'] = _ik8molhd.Endpoints()
|
||||
..initializeEndpoints(server);
|
||||
modules['serverpod_auth_idp'] = _iais.Endpoints()
|
||||
..initializeEndpoints(server);
|
||||
modules['serverpod_offline_sync'] = _izehhkf5.Endpoints()
|
||||
..initializeEndpoints(server);
|
||||
modules['halkyon_oidc'] = _ipoqfiqr.Endpoints()
|
||||
..initializeEndpoints(server);
|
||||
modules['serverpod_auth_core'] = _iacs.Endpoints()
|
||||
..initializeEndpoints(server);
|
||||
}
|
||||
|
||||
@override
|
||||
_is.FutureCallDispatch? get futureCalls {
|
||||
return _iluznlp5.FutureCalls();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
/* 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>{
|
||||
'PruningPruneAccountsFutureCall': PruningPruneAccountsFutureCall(),
|
||||
'PruningForgetSignUpAttemptsFutureCall':
|
||||
PruningForgetSignUpAttemptsFutureCall(),
|
||||
'PruningTellMembersOfDeletionsFutureCall':
|
||||
PruningTellMembersOfDeletionsFutureCall(),
|
||||
'PruningForgetDeletedAccountsFutureCall':
|
||||
PruningForgetDeletedAccountsFutureCall(),
|
||||
};
|
||||
_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> pruneAccounts() {
|
||||
return _invokeFutureCall(
|
||||
'PruningPruneAccountsFutureCall',
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> forgetSignUpAttempts() {
|
||||
return _invokeFutureCall(
|
||||
'PruningForgetSignUpAttemptsFutureCall',
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> tellMembersOfDeletions() {
|
||||
return _invokeFutureCall(
|
||||
'PruningTellMembersOfDeletionsFutureCall',
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> forgetDeletedAccounts() {
|
||||
return _invokeFutureCall(
|
||||
'PruningForgetDeletedAccountsFutureCall',
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Prunes one batch of anonymous accounts and logs how many people went,
|
||||
/// forgets the resets that lapsed (ADR 0017) and the assistants' figures
|
||||
/// past their 3 months (Learn's ADR 0021, D41), and deletes the new accounts left
|
||||
/// unapproved for 14 days, with the waiting passes past their time
|
||||
/// (Learn's ADR 0021, D25).
|
||||
class PruningPruneAccountsFutureCall extends _is.FutureCall
|
||||
implements _is.InvokableFutureCall {
|
||||
@override
|
||||
_ida.Future<void> invoke(
|
||||
_is.Session session,
|
||||
_is.SerializableModel? object,
|
||||
) async {
|
||||
await _ibrplims.PruningFutureCall().pruneAccounts(session);
|
||||
}
|
||||
}
|
||||
|
||||
/// Forgets the sign-up attempts the rate limit is done with, with their IP
|
||||
/// addresses, the email sign-in codes past their time, the password
|
||||
/// sign-ins never finished and authenticators never confirmed (ADR 0017),
|
||||
/// and the SimpleLogin sign-ins never finished (ADR 0020), 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// On the register, tells the instance's members again of the accounts
|
||||
/// gone (ADR 0016) and of the accounts whose sessions ended (ADR 0017: a
|
||||
/// password changed or reset) that they haven't confirmed yet, and logs how
|
||||
/// many confirmed.
|
||||
class PruningTellMembersOfDeletionsFutureCall extends _is.FutureCall
|
||||
implements _is.InvokableFutureCall {
|
||||
@override
|
||||
_ida.Future<void> invoke(
|
||||
_is.Session session,
|
||||
_is.SerializableModel? object,
|
||||
) async {
|
||||
await _ibrplims.PruningFutureCall().tellMembersOfDeletions(session);
|
||||
}
|
||||
}
|
||||
|
||||
/// On a member, deletes the people whose account the register no longer
|
||||
/// knows, and logs how many.
|
||||
class PruningForgetDeletedAccountsFutureCall extends _is.FutureCall
|
||||
implements _is.InvokableFutureCall {
|
||||
@override
|
||||
_ida.Future<void> invoke(
|
||||
_is.Session session,
|
||||
_is.SerializableModel? object,
|
||||
) async {
|
||||
await _ibrplims.PruningFutureCall().forgetDeletedAccounts(session);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,646 @@
|
|||
/* 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 'dart:typed_data' as _idt;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// A data key of the person's (ADR 0038, D7, as Train's ADR 0030, D15): the
|
||||
/// one their settings and sittings are sealed with. Sealed on a device
|
||||
/// to the account's X25519 key and signed by it (halkyon_keys'
|
||||
/// `sealDataKey`), under an ID that says what it is for, so only the
|
||||
/// person's devices open it and the instance can't relabel it; the instance
|
||||
/// keeps it as it is. Synced, so the key arrives with the data, works offline
|
||||
/// and goes with the space. Never updated.
|
||||
abstract class ContentKey
|
||||
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
|
||||
ContentKey._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
required this.dataKeyId,
|
||||
required this.sealedKey,
|
||||
});
|
||||
|
||||
factory ContentKey({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required String dataKeyId,
|
||||
required _idt.ByteData sealedKey,
|
||||
}) = _ContentKeyImpl;
|
||||
|
||||
factory ContentKey.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return ContentKey(
|
||||
id: jsonSerialization['id'] == null
|
||||
? null
|
||||
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
spaceId: jsonSerialization['spaceId'] as int?,
|
||||
dataKeyId: jsonSerialization['dataKeyId'] as String,
|
||||
sealedKey: _is.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['sealedKey'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static final t = ContentKeyTable();
|
||||
|
||||
static const db = ContentKeyRepository._();
|
||||
|
||||
@override
|
||||
_is.UuidValue? id;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
/// The key's ID, which the rows it seals name in their `keyId`; sealed
|
||||
/// with the key too, so the column is never trusted.
|
||||
String dataKeyId;
|
||||
|
||||
/// The sealed key: the sealer's Ed25519 signature, then the sealed box.
|
||||
_idt.ByteData sealedKey;
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [ContentKey]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
ContentKey copyWith({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? dataKeyId,
|
||||
_idt.ByteData? sealedKey,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'ContentKey',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'dataKeyId': dataKeyId,
|
||||
'sealedKey': sealedKey.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'ContentKey',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
'dataKeyId': dataKeyId,
|
||||
'sealedKey': sealedKey.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
static ContentKeyInclude include() {
|
||||
return ContentKeyInclude._();
|
||||
}
|
||||
|
||||
static ContentKeyIncludeList includeList({
|
||||
_is.WhereExpressionBuilder<ContentKeyTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<ContentKeyTable>? orderBy,
|
||||
_is.OrderByListBuilder<ContentKeyTable>? orderByList,
|
||||
ContentKeyInclude? include,
|
||||
}) {
|
||||
return ContentKeyIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(ContentKey.t),
|
||||
orderByList: orderByList?.call(ContentKey.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _ContentKeyImpl extends ContentKey {
|
||||
_ContentKeyImpl({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
required String dataKeyId,
|
||||
required _idt.ByteData sealedKey,
|
||||
}) : super._(
|
||||
id: id,
|
||||
spaceId: spaceId,
|
||||
dataKeyId: dataKeyId,
|
||||
sealedKey: sealedKey,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [ContentKey]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
ContentKey copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
String? dataKeyId,
|
||||
_idt.ByteData? sealedKey,
|
||||
}) {
|
||||
return ContentKey(
|
||||
id: id is _is.UuidValue? ? id : this.id,
|
||||
spaceId: spaceId is int? ? spaceId : this.spaceId,
|
||||
dataKeyId: dataKeyId ?? this.dataKeyId,
|
||||
sealedKey: sealedKey ?? this.sealedKey.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ContentKeyUpdateTable extends _is.UpdateTable<ContentKeyTable> {
|
||||
ContentKeyUpdateTable(super.table);
|
||||
|
||||
_is.ColumnValue<int, int> spaceId(int? value) => _is.ColumnValue(
|
||||
table.spaceId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> dataKeyId(String value) => _is.ColumnValue(
|
||||
table.dataKeyId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> sealedKey(
|
||||
_idt.ByteData value,
|
||||
) => _is.ColumnValue(
|
||||
table.sealedKey,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
class ContentKeyTable extends _is.Table<_is.UuidValue?> {
|
||||
ContentKeyTable({super.tableRelation}) : super(tableName: 'content_key') {
|
||||
updateTable = ContentKeyUpdateTable(this);
|
||||
spaceId = _is.ColumnInt(
|
||||
'spaceId',
|
||||
this,
|
||||
);
|
||||
dataKeyId = _is.ColumnString(
|
||||
'dataKeyId',
|
||||
this,
|
||||
);
|
||||
sealedKey = _is.ColumnByteData(
|
||||
'sealedKey',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final ContentKeyUpdateTable updateTable;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _is.ColumnInt spaceId;
|
||||
|
||||
/// The key's ID, which the rows it seals name in their `keyId`; sealed
|
||||
/// with the key too, so the column is never trusted.
|
||||
late final _is.ColumnString dataKeyId;
|
||||
|
||||
/// The sealed key: the sealer's Ed25519 signature, then the sealed box.
|
||||
late final _is.ColumnByteData sealedKey;
|
||||
|
||||
@override
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
spaceId,
|
||||
dataKeyId,
|
||||
sealedKey,
|
||||
];
|
||||
}
|
||||
|
||||
class ContentKeyInclude extends _is.IncludeObject {
|
||||
ContentKeyInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => ContentKey.t;
|
||||
}
|
||||
|
||||
class ContentKeyIncludeList extends _is.IncludeList {
|
||||
ContentKeyIncludeList._({
|
||||
_is.WhereExpressionBuilder<ContentKeyTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(ContentKey.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => ContentKey.t;
|
||||
}
|
||||
|
||||
class ContentKeyRepository {
|
||||
const ContentKeyRepository._();
|
||||
|
||||
/// Returns a list of [ContentKey]s matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order of the items use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// The maximum number of items can be set by [limit]. If no limit is set,
|
||||
/// all items matching the query will be returned.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which [limit] (or all)
|
||||
/// items are read from the database.
|
||||
///
|
||||
/// ```dart
|
||||
/// var persons = await Persons.db.find(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.firstName,
|
||||
/// limit: 100,
|
||||
/// );
|
||||
/// ```
|
||||
Future<List<ContentKey>> find(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<ContentKeyTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<ContentKeyTable>? orderBy,
|
||||
_is.OrderByListBuilder<ContentKeyTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<ContentKey>(
|
||||
where: where?.call(ContentKey.t),
|
||||
orderBy: orderBy?.call(ContentKey.t),
|
||||
orderByList: orderByList?.call(ContentKey.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [ContentKey] matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which the next one will be picked.
|
||||
///
|
||||
/// ```dart
|
||||
/// var youngestPerson = await Persons.db.findFirstRow(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.age,
|
||||
/// );
|
||||
/// ```
|
||||
Future<ContentKey?> findFirstRow(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<ContentKeyTable>? where,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<ContentKeyTable>? orderBy,
|
||||
_is.OrderByListBuilder<ContentKeyTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<ContentKey>(
|
||||
where: where?.call(ContentKey.t),
|
||||
orderBy: orderBy?.call(ContentKey.t),
|
||||
orderByList: orderByList?.call(ContentKey.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [ContentKey] by its [id] or null if no such row exists.
|
||||
Future<ContentKey?> findById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<ContentKey>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [ContentKey]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [ContentKey]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// insert, none of the rows will be inserted.
|
||||
///
|
||||
/// If [ignoreConflicts] is set to `true`, rows that conflict with existing
|
||||
/// rows are silently skipped, and only the successfully inserted rows are
|
||||
/// returned.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the inserted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<ContentKey>> insert(
|
||||
_is.DatabaseSession session,
|
||||
List<ContentKey> rows, {
|
||||
_is.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<ContentKey>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [ContentKey] and returns the inserted row.
|
||||
///
|
||||
/// The returned [ContentKey] will have its `id` field set.
|
||||
Future<ContentKey> insertRow(
|
||||
_is.DatabaseSession session,
|
||||
ContentKey row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<ContentKey>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [ContentKey]s in the list and returns the resulting rows.
|
||||
///
|
||||
/// If a row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated with the new values. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies to rows matching the
|
||||
/// given expression. Conflicting rows that don't match are skipped and not
|
||||
/// returned, so the resulting list may be shorter than [rows].
|
||||
///
|
||||
/// The returned [ContentKey]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails,
|
||||
/// none of the rows will be affected.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the resulting rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<ContentKey>> upsert(
|
||||
_is.DatabaseSession session,
|
||||
List<ContentKey> rows, {
|
||||
required _is.ColumnSelections<ContentKeyTable> conflictColumns,
|
||||
_is.ColumnSelections<ContentKeyTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<ContentKeyTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<ContentKey>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(ContentKey.t),
|
||||
updateColumns: updateColumns?.call(ContentKey.t),
|
||||
updateWhere: updateWhere?.call(ContentKey.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [ContentKey] and returns the resulting row.
|
||||
///
|
||||
/// If the row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies when the existing
|
||||
/// row matches the expression. Returns `null` if no row was affected — for
|
||||
/// example when [updateWhere] does not match the conflicting row.
|
||||
///
|
||||
/// The returned [ContentKey] will have its `id` field set.
|
||||
Future<ContentKey?> upsertRow(
|
||||
_is.DatabaseSession session,
|
||||
ContentKey row, {
|
||||
required _is.ColumnSelections<ContentKeyTable> conflictColumns,
|
||||
_is.ColumnSelections<ContentKeyTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<ContentKeyTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<ContentKey>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(ContentKey.t),
|
||||
updateColumns: updateColumns?.call(ContentKey.t),
|
||||
updateWhere: updateWhere?.call(ContentKey.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [ContentKey]s in the list and returns the updated rows. If
|
||||
/// [columns] is provided, only those columns will be updated. Defaults to
|
||||
/// all columns.
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// update, none of the rows will be updated.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<ContentKey>> update(
|
||||
_is.DatabaseSession session,
|
||||
List<ContentKey> rows, {
|
||||
_is.ColumnSelections<ContentKeyTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<ContentKey>(
|
||||
rows,
|
||||
columns: columns?.call(ContentKey.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [ContentKey]. The row needs to have its id set.
|
||||
/// Optionally, a list of [columns] can be provided to only update those
|
||||
/// columns. Defaults to all columns.
|
||||
Future<ContentKey> updateRow(
|
||||
_is.DatabaseSession session,
|
||||
ContentKey row, {
|
||||
_is.ColumnSelections<ContentKeyTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<ContentKey>(
|
||||
row,
|
||||
columns: columns?.call(ContentKey.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [ContentKey] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<ContentKey?> updateById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
required _is.ColumnValueListBuilder<ContentKeyUpdateTable> columnValues,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<ContentKey>(
|
||||
id,
|
||||
columnValues: columnValues(ContentKey.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [ContentKey]s matching the [where] expression with the specified [columnValues].
|
||||
/// Returns the list of updated rows.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<ContentKey>> updateWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.ColumnValueListBuilder<ContentKeyUpdateTable> columnValues,
|
||||
required _is.WhereExpressionBuilder<ContentKeyTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<ContentKeyTable>? orderBy,
|
||||
_is.OrderByListBuilder<ContentKeyTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<ContentKey>(
|
||||
columnValues: columnValues(ContentKey.t.updateTable),
|
||||
where: where(ContentKey.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(ContentKey.t),
|
||||
orderByList: orderByList?.call(ContentKey.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [ContentKey]s in the list and returns the deleted rows.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fail to
|
||||
/// be deleted, none of the rows will be deleted.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<ContentKey>> delete(
|
||||
_is.DatabaseSession session,
|
||||
List<ContentKey> rows, {
|
||||
_is.OrderByBuilder<ContentKeyTable>? orderBy,
|
||||
_is.OrderByListBuilder<ContentKeyTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<ContentKey>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(ContentKey.t),
|
||||
orderByList: orderByList?.call(ContentKey.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [ContentKey].
|
||||
Future<ContentKey> deleteRow(
|
||||
_is.DatabaseSession session,
|
||||
ContentKey row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<ContentKey>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all rows matching the [where] expression.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<ContentKey>> deleteWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<ContentKeyTable> where,
|
||||
_is.OrderByBuilder<ContentKeyTable>? orderBy,
|
||||
_is.OrderByListBuilder<ContentKeyTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<ContentKey>(
|
||||
where: where(ContentKey.t),
|
||||
orderBy: orderBy?.call(ContentKey.t),
|
||||
orderByList: orderByList?.call(ContentKey.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Counts the number of rows matching the [where] expression. If omitted,
|
||||
/// will return the count of all rows in the table.
|
||||
Future<int> count(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<ContentKeyTable>? where,
|
||||
int? limit,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<ContentKey>(
|
||||
where: where?.call(ContentKey.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [ContentKey] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<ContentKeyTable> where,
|
||||
required _is.LockMode lockMode,
|
||||
required _is.Transaction transaction,
|
||||
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<ContentKey>(
|
||||
where: where(ContentKey.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,504 @@
|
|||
/* 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: 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:halkyon_oidc_server/halkyon_oidc_server.dart' as _ipoqfiqr;
|
||||
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 'package:serverpod_offline_sync_server/serverpod_offline_sync_server.dart'
|
||||
as _izehhkf5;
|
||||
import 'privacy/content_key.dart' as _imed3x5d;
|
||||
import 'settings/meditate_settings.dart' as _iutacwgf;
|
||||
import 'sitting/sitting.dart' as _ic8w2fbu;
|
||||
export 'privacy/content_key.dart';
|
||||
export 'settings/meditate_settings.dart';
|
||||
export 'sitting/sitting.dart';
|
||||
export 'sync_tables.dart';
|
||||
|
||||
class Protocol extends _is.DatabaseSerializationManager {
|
||||
Protocol._();
|
||||
|
||||
factory Protocol() => _instance;
|
||||
|
||||
static final Protocol _instance = Protocol._().._registerHostProtocols();
|
||||
|
||||
static List<_isp.TableDefinition> get targetTableDefinitions => [
|
||||
_isp.TableDefinition(
|
||||
name: 'content_key',
|
||||
dartName: 'ContentKey',
|
||||
schema: 'public',
|
||||
module: 'halkyon_meditate',
|
||||
columns: [
|
||||
_isp.ColumnDefinition(
|
||||
name: 'id',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue?',
|
||||
columnDefault: 'random_v7',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'spaceId',
|
||||
columnType: _isp.ColumnType.bigint,
|
||||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'dataKeyId',
|
||||
columnType: _isp.ColumnType.text,
|
||||
isNullable: false,
|
||||
dartType: 'String',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'sealedKey',
|
||||
columnType: _isp.ColumnType.bytea,
|
||||
isNullable: false,
|
||||
dartType: 'dart:typed_data:ByteData',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'content_key_fk_0',
|
||||
columns: ['spaceId'],
|
||||
referenceTable: 'offline_sync_spaces',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'content_key_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isp.TableDefinition(
|
||||
name: 'meditate_settings',
|
||||
dartName: 'MeditateSettings',
|
||||
schema: 'public',
|
||||
module: 'halkyon_meditate',
|
||||
columns: [
|
||||
_isp.ColumnDefinition(
|
||||
name: 'id',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue?',
|
||||
columnDefault: 'random_v7',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'spaceId',
|
||||
columnType: _isp.ColumnType.bigint,
|
||||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'keyId',
|
||||
columnType: _isp.ColumnType.text,
|
||||
isNullable: true,
|
||||
dartType: 'String?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'payload',
|
||||
columnType: _isp.ColumnType.bytea,
|
||||
isNullable: false,
|
||||
dartType: 'dart:typed_data:ByteData',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'nonce',
|
||||
columnType: _isp.ColumnType.bytea,
|
||||
isNullable: true,
|
||||
dartType: 'dart:typed_data:ByteData?',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'meditate_settings_fk_0',
|
||||
columns: ['spaceId'],
|
||||
referenceTable: 'offline_sync_spaces',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'meditate_settings_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isp.TableDefinition(
|
||||
name: 'sitting',
|
||||
dartName: 'Sitting',
|
||||
schema: 'public',
|
||||
module: 'halkyon_meditate',
|
||||
columns: [
|
||||
_isp.ColumnDefinition(
|
||||
name: 'id',
|
||||
columnType: _isp.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue?',
|
||||
columnDefault: 'random_v7',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'spaceId',
|
||||
columnType: _isp.ColumnType.bigint,
|
||||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'keyId',
|
||||
columnType: _isp.ColumnType.text,
|
||||
isNullable: true,
|
||||
dartType: 'String?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'payload',
|
||||
columnType: _isp.ColumnType.bytea,
|
||||
isNullable: false,
|
||||
dartType: 'dart:typed_data:ByteData',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'nonce',
|
||||
columnType: _isp.ColumnType.bytea,
|
||||
isNullable: true,
|
||||
dartType: 'dart:typed_data:ByteData?',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isp.ForeignKeyDefinition(
|
||||
constraintName: 'sitting_fk_0',
|
||||
columns: ['spaceId'],
|
||||
referenceTable: 'offline_sync_spaces',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isp.ForeignKeyAction.noAction,
|
||||
onDelete: _isp.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isp.IndexDefinition(
|
||||
indexName: 'sitting_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isp.IndexElementDefinition(
|
||||
type: _isp.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
..._ik8molhd.Protocol.targetTableDefinitions,
|
||||
..._iais.Protocol.targetTableDefinitions,
|
||||
..._izehhkf5.Protocol.targetTableDefinitions,
|
||||
..._ipoqfiqr.Protocol.targetTableDefinitions,
|
||||
..._iacs.Protocol.targetTableDefinitions,
|
||||
..._isp.Protocol.targetTableDefinitions,
|
||||
];
|
||||
|
||||
static String? getClassNameFromObjectJson(dynamic data) {
|
||||
if (data is! Map) return null;
|
||||
final className = data['__className__'] as String?;
|
||||
return className;
|
||||
}
|
||||
|
||||
@override
|
||||
T deserialize<T>(
|
||||
dynamic data, [
|
||||
Type? t,
|
||||
]) {
|
||||
t ??= T;
|
||||
|
||||
final dataClassName = getClassNameFromObjectJson(data);
|
||||
if (dataClassName != null && dataClassName != getClassNameForType(t)) {
|
||||
try {
|
||||
return deserializeByClassName({
|
||||
'className': dataClassName,
|
||||
'data': data,
|
||||
});
|
||||
} on _is.DeserializationClassNameNotFoundException catch (_) {
|
||||
// If the className is not recognized (e.g., older client receiving
|
||||
// data with a new subtype), fall back to deserializing without the
|
||||
// className, using the expected type T.
|
||||
}
|
||||
}
|
||||
|
||||
if (t == _imed3x5d.ContentKey) {
|
||||
return _imed3x5d.ContentKey.fromJson(data) as T;
|
||||
}
|
||||
if (t == _iutacwgf.MeditateSettings) {
|
||||
return _iutacwgf.MeditateSettings.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ic8w2fbu.Sitting) {
|
||||
return _ic8w2fbu.Sitting.fromJson(data) as T;
|
||||
}
|
||||
if (t == _is.getType<_imed3x5d.ContentKey?>()) {
|
||||
return (data != null ? _imed3x5d.ContentKey.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _is.getType<_iutacwgf.MeditateSettings?>()) {
|
||||
return (data != null ? _iutacwgf.MeditateSettings.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_ic8w2fbu.Sitting?>()) {
|
||||
return (data != null ? _ic8w2fbu.Sitting.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 (_) {}
|
||||
try {
|
||||
return _izehhkf5.Protocol().deserialize<T>(data, t);
|
||||
} on _is.DeserializationTypeNotFoundException catch (_) {}
|
||||
try {
|
||||
return _ipoqfiqr.Protocol().deserialize<T>(data, t);
|
||||
} on _is.DeserializationTypeNotFoundException catch (_) {}
|
||||
try {
|
||||
return _iacs.Protocol().deserialize<T>(data, t);
|
||||
} on _is.DeserializationTypeNotFoundException catch (_) {}
|
||||
try {
|
||||
return _isp.Protocol().deserialize<T>(data, t);
|
||||
} on _is.DeserializationTypeNotFoundException catch (_) {}
|
||||
return super.deserialize<T>(data, t);
|
||||
}
|
||||
|
||||
static String? getClassNameForType(Type type) {
|
||||
return switch (type) {
|
||||
_imed3x5d.ContentKey => 'ContentKey',
|
||||
_iutacwgf.MeditateSettings => 'MeditateSettings',
|
||||
_ic8w2fbu.Sitting => 'Sitting',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String? getClassNameForObject(Object? data) {
|
||||
String? className = super.getClassNameForObject(data);
|
||||
if (className != null) return className;
|
||||
|
||||
if (data is Map<String, dynamic> && data['__className__'] is String) {
|
||||
return (data['__className__'] as String).replaceFirst(
|
||||
'halkyon_meditate.',
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
switch (data) {
|
||||
case _imed3x5d.ContentKey():
|
||||
return 'ContentKey';
|
||||
case _iutacwgf.MeditateSettings():
|
||||
return 'MeditateSettings';
|
||||
case _ic8w2fbu.Sitting():
|
||||
return 'Sitting';
|
||||
}
|
||||
className = _ik8molhd.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
return className.contains('.') ? className : 'halkyon_base.$className';
|
||||
}
|
||||
className = _iais.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
return className.contains('.')
|
||||
? className
|
||||
: 'serverpod_auth_idp.$className';
|
||||
}
|
||||
className = _izehhkf5.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
return className.contains('.')
|
||||
? className
|
||||
: 'serverpod_offline_sync.$className';
|
||||
}
|
||||
className = _ipoqfiqr.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
return className.contains('.') ? className : 'halkyon_oidc.$className';
|
||||
}
|
||||
className = _iacs.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
return className.contains('.')
|
||||
? className
|
||||
: 'serverpod_auth_core.$className';
|
||||
}
|
||||
className = _isp.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
return className.contains('.') ? className : 'serverpod.$className';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic deserializeByClassName(Map<String, dynamic> data) {
|
||||
var dataClassName = data['className'];
|
||||
if (dataClassName is! String) {
|
||||
return super.deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName == 'ContentKey') {
|
||||
return deserialize<_imed3x5d.ContentKey>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'MeditateSettings') {
|
||||
return deserialize<_iutacwgf.MeditateSettings>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'Sitting') {
|
||||
return deserialize<_ic8w2fbu.Sitting>(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);
|
||||
return _iais.Protocol().deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName.startsWith('serverpod_offline_sync.')) {
|
||||
data['className'] = dataClassName.substring(23);
|
||||
return _izehhkf5.Protocol().deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName.startsWith('halkyon_oidc.')) {
|
||||
data['className'] = dataClassName.substring(13);
|
||||
return _ipoqfiqr.Protocol().deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName.startsWith('serverpod_auth_core.')) {
|
||||
data['className'] = dataClassName.substring(20);
|
||||
return _iacs.Protocol().deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName.startsWith('serverpod.')) {
|
||||
data['className'] = dataClassName.substring(10);
|
||||
return _isp.Protocol().deserializeByClassName(data);
|
||||
}
|
||||
return super.deserializeByClassName(data);
|
||||
}
|
||||
|
||||
void _registerHostProtocols() {
|
||||
_ik8molhd.Protocol().registerHostProtocol('halkyon_meditate', this);
|
||||
_iais.Protocol().registerHostProtocol('halkyon_meditate', this);
|
||||
_izehhkf5.Protocol().registerHostProtocol('halkyon_meditate', this);
|
||||
_ipoqfiqr.Protocol().registerHostProtocol('halkyon_meditate', this);
|
||||
_iacs.Protocol().registerHostProtocol('halkyon_meditate', 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) {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
{
|
||||
var table = _izehhkf5.Protocol().getTableForType(t);
|
||||
if (table != null) {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
{
|
||||
var table = _ipoqfiqr.Protocol().getTableForType(t);
|
||||
if (table != null) {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
{
|
||||
var table = _iacs.Protocol().getTableForType(t);
|
||||
if (table != null) {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
{
|
||||
var table = _isp.Protocol().getTableForType(t);
|
||||
if (table != null) {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
switch (t) {
|
||||
case _imed3x5d.ContentKey:
|
||||
return _imed3x5d.ContentKey.t;
|
||||
case _iutacwgf.MeditateSettings:
|
||||
return _iutacwgf.MeditateSettings.t;
|
||||
case _ic8w2fbu.Sitting:
|
||||
return _ic8w2fbu.Sitting.t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
List<_isp.TableDefinition> getTargetTableDefinitions() =>
|
||||
targetTableDefinitions;
|
||||
|
||||
@override
|
||||
String getModuleName() => 'halkyon_meditate';
|
||||
|
||||
/// Maps any `Record`s known to this [Protocol] to their JSON representation
|
||||
///
|
||||
/// Throws in case the record type is not known.
|
||||
///
|
||||
/// This method will return `null` (only) for `null` inputs.
|
||||
Map<String, dynamic>? mapRecordToJson(Record? record) {
|
||||
if (record == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return _ik8molhd.Protocol().mapRecordToJson(record);
|
||||
} catch (_) {}
|
||||
try {
|
||||
return _iais.Protocol().mapRecordToJson(record);
|
||||
} catch (_) {}
|
||||
try {
|
||||
return _izehhkf5.Protocol().mapRecordToJson(record);
|
||||
} catch (_) {}
|
||||
try {
|
||||
return _ipoqfiqr.Protocol().mapRecordToJson(record);
|
||||
} catch (_) {}
|
||||
try {
|
||||
return _iacs.Protocol().mapRecordToJson(record);
|
||||
} catch (_) {}
|
||||
throw Exception('Unsupported record type ${record.runtimeType}');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/* 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 'dart:io' as _idi;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
import 'package:serverpod_offline_sync_server/serverpod_offline_sync_server.dart'
|
||||
as _izehhkf5;
|
||||
import 'endpoints.dart' as _iavctuc6;
|
||||
import 'protocol.dart' as _il2as5qe;
|
||||
import 'sync_tables.dart' as _ii3f3u05;
|
||||
export 'package:serverpod/serverpod.dart' hide Serverpod;
|
||||
export 'future_calls.dart' show ServerpodFutureCallsGetter;
|
||||
|
||||
/// The Serverpod server for this project.
|
||||
///
|
||||
/// Pre-configured with the generated Protocol serialization manager and
|
||||
/// Endpoints, so a server can be created with just the command line
|
||||
/// arguments:
|
||||
///
|
||||
/// ```dart
|
||||
/// final pod = Serverpod(args);
|
||||
/// ```
|
||||
///
|
||||
/// The `serverpod_offline_sync` engine is initialized with the tables
|
||||
/// declared with `database: sync`, wrapping any provided
|
||||
/// [databaseInterceptor] with `offlineSyncDatabaseInterceptor`.
|
||||
class Serverpod extends _is.Serverpod {
|
||||
Serverpod(
|
||||
List<String> args, {
|
||||
_idi.Directory? serverDirectory,
|
||||
_is.ServerpodConfig? config,
|
||||
_is.ServerpodConfig Function(_is.ServerpodConfig)? configOverride,
|
||||
_is.AuthenticationHandler? authenticationHandler,
|
||||
_is.HealthCheckHandler? healthCheckHandler,
|
||||
_is.HealthConfig? healthConfig,
|
||||
_is.Headers? httpResponseHeaders,
|
||||
_is.Headers? httpOptionsResponseHeaders,
|
||||
_is.SecurityContextConfig? securityContextConfig,
|
||||
_is.ExperimentalFeatures? experimentalFeatures,
|
||||
_is.RuntimeParametersListBuilder? runtimeParametersBuilder,
|
||||
_is.DatabaseInterceptor? databaseInterceptor,
|
||||
}) : super(
|
||||
args,
|
||||
_il2as5qe.Protocol(),
|
||||
_iavctuc6.Endpoints(),
|
||||
serverDirectory: serverDirectory,
|
||||
config: config,
|
||||
configOverride: configOverride,
|
||||
authenticationHandler: authenticationHandler,
|
||||
healthCheckHandler: healthCheckHandler,
|
||||
healthConfig: healthConfig,
|
||||
httpResponseHeaders: httpResponseHeaders,
|
||||
httpOptionsResponseHeaders: httpOptionsResponseHeaders,
|
||||
securityContextConfig: securityContextConfig,
|
||||
experimentalFeatures: experimentalFeatures,
|
||||
runtimeParametersBuilder: runtimeParametersBuilder,
|
||||
databaseInterceptor:
|
||||
(
|
||||
session,
|
||||
inner,
|
||||
) => _izehhkf5.offlineSyncDatabaseInterceptor(
|
||||
session,
|
||||
databaseInterceptor?.call(
|
||||
session,
|
||||
inner,
|
||||
) ??
|
||||
inner,
|
||||
),
|
||||
) {
|
||||
initializeOfflineSync(syncTables: _ii3f3u05.syncTables);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,674 @@
|
|||
/* 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 'dart:typed_data' as _idt;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// The person's settings of Meditate's (ADR 0038, D7, D11): one row of theirs,
|
||||
/// under an ID keyed by their account, holding the bells, the screen kept on,
|
||||
/// the timer's length, the favourite practices and whether the streak shows in
|
||||
/// its sealed payload; the instance sees that it exists, its size and when it
|
||||
/// changed. The last writer wins. Synced.
|
||||
abstract class MeditateSettings
|
||||
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
|
||||
MeditateSettings._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
this.keyId,
|
||||
required this.payload,
|
||||
this.nonce,
|
||||
});
|
||||
|
||||
factory MeditateSettings({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) = _MeditateSettingsImpl;
|
||||
|
||||
factory MeditateSettings.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return MeditateSettings(
|
||||
id: jsonSerialization['id'] == null
|
||||
? null
|
||||
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
spaceId: jsonSerialization['spaceId'] as int?,
|
||||
keyId: jsonSerialization['keyId'] as String?,
|
||||
payload: _is.ByteDataJsonExtension.fromJson(jsonSerialization['payload']),
|
||||
nonce: jsonSerialization['nonce'] == null
|
||||
? null
|
||||
: _is.ByteDataJsonExtension.fromJson(jsonSerialization['nonce']),
|
||||
);
|
||||
}
|
||||
|
||||
static final t = MeditateSettingsTable();
|
||||
|
||||
static const db = MeditateSettingsRepository._();
|
||||
|
||||
@override
|
||||
_is.UuidValue? id;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
/// The key it is sealed with (ADR 0002's envelope); never null in Meditate,
|
||||
/// where everything is sealed.
|
||||
String? keyId;
|
||||
|
||||
/// The content, sealed: UTF-8 JSON with a `schemaVersion` once opened.
|
||||
_idt.ByteData payload;
|
||||
|
||||
/// The sealing's nonce (ADR 0002's envelope).
|
||||
_idt.ByteData? nonce;
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [MeditateSettings]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
MeditateSettings copyWith({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
_idt.ByteData? payload,
|
||||
_idt.ByteData? nonce,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'MeditateSettings',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'MeditateSettings',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
static MeditateSettingsInclude include() {
|
||||
return MeditateSettingsInclude._();
|
||||
}
|
||||
|
||||
static MeditateSettingsIncludeList includeList({
|
||||
_is.WhereExpressionBuilder<MeditateSettingsTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<MeditateSettingsTable>? orderBy,
|
||||
_is.OrderByListBuilder<MeditateSettingsTable>? orderByList,
|
||||
MeditateSettingsInclude? include,
|
||||
}) {
|
||||
return MeditateSettingsIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(MeditateSettings.t),
|
||||
orderByList: orderByList?.call(MeditateSettings.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _MeditateSettingsImpl extends MeditateSettings {
|
||||
_MeditateSettingsImpl({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) : super._(
|
||||
id: id,
|
||||
spaceId: spaceId,
|
||||
keyId: keyId,
|
||||
payload: payload,
|
||||
nonce: nonce,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [MeditateSettings]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
MeditateSettings copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
Object? keyId = _Undefined,
|
||||
_idt.ByteData? payload,
|
||||
Object? nonce = _Undefined,
|
||||
}) {
|
||||
return MeditateSettings(
|
||||
id: id is _is.UuidValue? ? id : this.id,
|
||||
spaceId: spaceId is int? ? spaceId : this.spaceId,
|
||||
keyId: keyId is String? ? keyId : this.keyId,
|
||||
payload: payload ?? this.payload.clone(),
|
||||
nonce: nonce is _idt.ByteData? ? nonce : this.nonce?.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MeditateSettingsUpdateTable
|
||||
extends _is.UpdateTable<MeditateSettingsTable> {
|
||||
MeditateSettingsUpdateTable(super.table);
|
||||
|
||||
_is.ColumnValue<int, int> spaceId(int? value) => _is.ColumnValue(
|
||||
table.spaceId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> keyId(String? value) => _is.ColumnValue(
|
||||
table.keyId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> payload(_idt.ByteData value) =>
|
||||
_is.ColumnValue(
|
||||
table.payload,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> nonce(_idt.ByteData? value) =>
|
||||
_is.ColumnValue(
|
||||
table.nonce,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
class MeditateSettingsTable extends _is.Table<_is.UuidValue?> {
|
||||
MeditateSettingsTable({super.tableRelation})
|
||||
: super(tableName: 'meditate_settings') {
|
||||
updateTable = MeditateSettingsUpdateTable(this);
|
||||
spaceId = _is.ColumnInt(
|
||||
'spaceId',
|
||||
this,
|
||||
);
|
||||
keyId = _is.ColumnString(
|
||||
'keyId',
|
||||
this,
|
||||
);
|
||||
payload = _is.ColumnByteData(
|
||||
'payload',
|
||||
this,
|
||||
);
|
||||
nonce = _is.ColumnByteData(
|
||||
'nonce',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final MeditateSettingsUpdateTable updateTable;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _is.ColumnInt spaceId;
|
||||
|
||||
/// The key it is sealed with (ADR 0002's envelope); never null in Meditate,
|
||||
/// where everything is sealed.
|
||||
late final _is.ColumnString keyId;
|
||||
|
||||
/// The content, sealed: UTF-8 JSON with a `schemaVersion` once opened.
|
||||
late final _is.ColumnByteData payload;
|
||||
|
||||
/// The sealing's nonce (ADR 0002's envelope).
|
||||
late final _is.ColumnByteData nonce;
|
||||
|
||||
@override
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
spaceId,
|
||||
keyId,
|
||||
payload,
|
||||
nonce,
|
||||
];
|
||||
}
|
||||
|
||||
class MeditateSettingsInclude extends _is.IncludeObject {
|
||||
MeditateSettingsInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => MeditateSettings.t;
|
||||
}
|
||||
|
||||
class MeditateSettingsIncludeList extends _is.IncludeList {
|
||||
MeditateSettingsIncludeList._({
|
||||
_is.WhereExpressionBuilder<MeditateSettingsTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(MeditateSettings.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => MeditateSettings.t;
|
||||
}
|
||||
|
||||
class MeditateSettingsRepository {
|
||||
const MeditateSettingsRepository._();
|
||||
|
||||
/// Returns a list of [MeditateSettings]s matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order of the items use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// The maximum number of items can be set by [limit]. If no limit is set,
|
||||
/// all items matching the query will be returned.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which [limit] (or all)
|
||||
/// items are read from the database.
|
||||
///
|
||||
/// ```dart
|
||||
/// var persons = await Persons.db.find(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.firstName,
|
||||
/// limit: 100,
|
||||
/// );
|
||||
/// ```
|
||||
Future<List<MeditateSettings>> find(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<MeditateSettingsTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<MeditateSettingsTable>? orderBy,
|
||||
_is.OrderByListBuilder<MeditateSettingsTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<MeditateSettings>(
|
||||
where: where?.call(MeditateSettings.t),
|
||||
orderBy: orderBy?.call(MeditateSettings.t),
|
||||
orderByList: orderByList?.call(MeditateSettings.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [MeditateSettings] matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which the next one will be picked.
|
||||
///
|
||||
/// ```dart
|
||||
/// var youngestPerson = await Persons.db.findFirstRow(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.age,
|
||||
/// );
|
||||
/// ```
|
||||
Future<MeditateSettings?> findFirstRow(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<MeditateSettingsTable>? where,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<MeditateSettingsTable>? orderBy,
|
||||
_is.OrderByListBuilder<MeditateSettingsTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<MeditateSettings>(
|
||||
where: where?.call(MeditateSettings.t),
|
||||
orderBy: orderBy?.call(MeditateSettings.t),
|
||||
orderByList: orderByList?.call(MeditateSettings.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [MeditateSettings] by its [id] or null if no such row exists.
|
||||
Future<MeditateSettings?> findById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<MeditateSettings>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [MeditateSettings]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [MeditateSettings]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// insert, none of the rows will be inserted.
|
||||
///
|
||||
/// If [ignoreConflicts] is set to `true`, rows that conflict with existing
|
||||
/// rows are silently skipped, and only the successfully inserted rows are
|
||||
/// returned.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the inserted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<MeditateSettings>> insert(
|
||||
_is.DatabaseSession session,
|
||||
List<MeditateSettings> rows, {
|
||||
_is.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<MeditateSettings>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [MeditateSettings] and returns the inserted row.
|
||||
///
|
||||
/// The returned [MeditateSettings] will have its `id` field set.
|
||||
Future<MeditateSettings> insertRow(
|
||||
_is.DatabaseSession session,
|
||||
MeditateSettings row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<MeditateSettings>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [MeditateSettings]s in the list and returns the resulting rows.
|
||||
///
|
||||
/// If a row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated with the new values. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies to rows matching the
|
||||
/// given expression. Conflicting rows that don't match are skipped and not
|
||||
/// returned, so the resulting list may be shorter than [rows].
|
||||
///
|
||||
/// The returned [MeditateSettings]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails,
|
||||
/// none of the rows will be affected.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the resulting rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<MeditateSettings>> upsert(
|
||||
_is.DatabaseSession session,
|
||||
List<MeditateSettings> rows, {
|
||||
required _is.ColumnSelections<MeditateSettingsTable> conflictColumns,
|
||||
_is.ColumnSelections<MeditateSettingsTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<MeditateSettingsTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<MeditateSettings>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(MeditateSettings.t),
|
||||
updateColumns: updateColumns?.call(MeditateSettings.t),
|
||||
updateWhere: updateWhere?.call(MeditateSettings.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [MeditateSettings] and returns the resulting row.
|
||||
///
|
||||
/// If the row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies when the existing
|
||||
/// row matches the expression. Returns `null` if no row was affected — for
|
||||
/// example when [updateWhere] does not match the conflicting row.
|
||||
///
|
||||
/// The returned [MeditateSettings] will have its `id` field set.
|
||||
Future<MeditateSettings?> upsertRow(
|
||||
_is.DatabaseSession session,
|
||||
MeditateSettings row, {
|
||||
required _is.ColumnSelections<MeditateSettingsTable> conflictColumns,
|
||||
_is.ColumnSelections<MeditateSettingsTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<MeditateSettingsTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<MeditateSettings>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(MeditateSettings.t),
|
||||
updateColumns: updateColumns?.call(MeditateSettings.t),
|
||||
updateWhere: updateWhere?.call(MeditateSettings.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [MeditateSettings]s in the list and returns the updated rows. If
|
||||
/// [columns] is provided, only those columns will be updated. Defaults to
|
||||
/// all columns.
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// update, none of the rows will be updated.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<MeditateSettings>> update(
|
||||
_is.DatabaseSession session,
|
||||
List<MeditateSettings> rows, {
|
||||
_is.ColumnSelections<MeditateSettingsTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<MeditateSettings>(
|
||||
rows,
|
||||
columns: columns?.call(MeditateSettings.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [MeditateSettings]. The row needs to have its id set.
|
||||
/// Optionally, a list of [columns] can be provided to only update those
|
||||
/// columns. Defaults to all columns.
|
||||
Future<MeditateSettings> updateRow(
|
||||
_is.DatabaseSession session,
|
||||
MeditateSettings row, {
|
||||
_is.ColumnSelections<MeditateSettingsTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<MeditateSettings>(
|
||||
row,
|
||||
columns: columns?.call(MeditateSettings.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [MeditateSettings] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<MeditateSettings?> updateById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
required _is.ColumnValueListBuilder<MeditateSettingsUpdateTable>
|
||||
columnValues,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<MeditateSettings>(
|
||||
id,
|
||||
columnValues: columnValues(MeditateSettings.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [MeditateSettings]s matching the [where] expression with the specified [columnValues].
|
||||
/// Returns the list of updated rows.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<MeditateSettings>> updateWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.ColumnValueListBuilder<MeditateSettingsUpdateTable>
|
||||
columnValues,
|
||||
required _is.WhereExpressionBuilder<MeditateSettingsTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<MeditateSettingsTable>? orderBy,
|
||||
_is.OrderByListBuilder<MeditateSettingsTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<MeditateSettings>(
|
||||
columnValues: columnValues(MeditateSettings.t.updateTable),
|
||||
where: where(MeditateSettings.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(MeditateSettings.t),
|
||||
orderByList: orderByList?.call(MeditateSettings.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [MeditateSettings]s in the list and returns the deleted rows.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fail to
|
||||
/// be deleted, none of the rows will be deleted.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<MeditateSettings>> delete(
|
||||
_is.DatabaseSession session,
|
||||
List<MeditateSettings> rows, {
|
||||
_is.OrderByBuilder<MeditateSettingsTable>? orderBy,
|
||||
_is.OrderByListBuilder<MeditateSettingsTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<MeditateSettings>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(MeditateSettings.t),
|
||||
orderByList: orderByList?.call(MeditateSettings.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [MeditateSettings].
|
||||
Future<MeditateSettings> deleteRow(
|
||||
_is.DatabaseSession session,
|
||||
MeditateSettings row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<MeditateSettings>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all rows matching the [where] expression.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<MeditateSettings>> deleteWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<MeditateSettingsTable> where,
|
||||
_is.OrderByBuilder<MeditateSettingsTable>? orderBy,
|
||||
_is.OrderByListBuilder<MeditateSettingsTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<MeditateSettings>(
|
||||
where: where(MeditateSettings.t),
|
||||
orderBy: orderBy?.call(MeditateSettings.t),
|
||||
orderByList: orderByList?.call(MeditateSettings.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Counts the number of rows matching the [where] expression. If omitted,
|
||||
/// will return the count of all rows in the table.
|
||||
Future<int> count(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<MeditateSettingsTable>? where,
|
||||
int? limit,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<MeditateSettings>(
|
||||
where: where?.call(MeditateSettings.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [MeditateSettings] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<MeditateSettingsTable> where,
|
||||
required _is.LockMode lockMode,
|
||||
required _is.Transaction transaction,
|
||||
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<MeditateSettings>(
|
||||
where: where(MeditateSettings.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,671 @@
|
|||
/* 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 'dart:typed_data' as _idt;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// A sitting the person kept (ADR 0038, D7): the practice or the timer, when
|
||||
/// it started (a wall-clock time with the device's offset), how long it was
|
||||
/// planned and how long was sat, all in its sealed payload; the instance sees
|
||||
/// that it exists, its size and when it changed. Written once, deleted by the
|
||||
/// person alone. Called a sitting, as Serverpod's `Session` is every
|
||||
/// endpoint's. Synced.
|
||||
abstract class Sitting
|
||||
implements _is.TableRow<_is.UuidValue?>, _is.ProtocolSerialization {
|
||||
Sitting._({
|
||||
this.id,
|
||||
this.spaceId,
|
||||
this.keyId,
|
||||
required this.payload,
|
||||
this.nonce,
|
||||
});
|
||||
|
||||
factory Sitting({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) = _SittingImpl;
|
||||
|
||||
factory Sitting.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return Sitting(
|
||||
id: jsonSerialization['id'] == null
|
||||
? null
|
||||
: _is.UuidValueJsonExtension.fromJson(jsonSerialization['id']),
|
||||
spaceId: jsonSerialization['spaceId'] as int?,
|
||||
keyId: jsonSerialization['keyId'] as String?,
|
||||
payload: _is.ByteDataJsonExtension.fromJson(jsonSerialization['payload']),
|
||||
nonce: jsonSerialization['nonce'] == null
|
||||
? null
|
||||
: _is.ByteDataJsonExtension.fromJson(jsonSerialization['nonce']),
|
||||
);
|
||||
}
|
||||
|
||||
static final t = SittingTable();
|
||||
|
||||
static const db = SittingRepository._();
|
||||
|
||||
@override
|
||||
_is.UuidValue? id;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
int? spaceId;
|
||||
|
||||
/// The key it is sealed with (ADR 0002's envelope); never null in Meditate,
|
||||
/// where everything is sealed.
|
||||
String? keyId;
|
||||
|
||||
/// The content, sealed: UTF-8 JSON with a `schemaVersion` once opened.
|
||||
_idt.ByteData payload;
|
||||
|
||||
/// The sealing's nonce (ADR 0002's envelope).
|
||||
_idt.ByteData? nonce;
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => t;
|
||||
|
||||
/// Returns a shallow copy of this [Sitting]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
Sitting copyWith({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
_idt.ByteData? payload,
|
||||
_idt.ByteData? nonce,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'Sitting',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'Sitting',
|
||||
if (id != null) 'id': id?.toJson(),
|
||||
if (spaceId != null) 'spaceId': spaceId,
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
static SittingInclude include() {
|
||||
return SittingInclude._();
|
||||
}
|
||||
|
||||
static SittingIncludeList includeList({
|
||||
_is.WhereExpressionBuilder<SittingTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<SittingTable>? orderBy,
|
||||
_is.OrderByListBuilder<SittingTable>? orderByList,
|
||||
SittingInclude? include,
|
||||
}) {
|
||||
return SittingIncludeList._(
|
||||
where: where,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Sitting.t),
|
||||
orderByList: orderByList?.call(Sitting.t),
|
||||
include: include,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _is.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _SittingImpl extends Sitting {
|
||||
_SittingImpl({
|
||||
_is.UuidValue? id,
|
||||
int? spaceId,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
}) : super._(
|
||||
id: id,
|
||||
spaceId: spaceId,
|
||||
keyId: keyId,
|
||||
payload: payload,
|
||||
nonce: nonce,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [Sitting]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_is.useResult
|
||||
@override
|
||||
Sitting copyWith({
|
||||
Object? id = _Undefined,
|
||||
Object? spaceId = _Undefined,
|
||||
Object? keyId = _Undefined,
|
||||
_idt.ByteData? payload,
|
||||
Object? nonce = _Undefined,
|
||||
}) {
|
||||
return Sitting(
|
||||
id: id is _is.UuidValue? ? id : this.id,
|
||||
spaceId: spaceId is int? ? spaceId : this.spaceId,
|
||||
keyId: keyId is String? ? keyId : this.keyId,
|
||||
payload: payload ?? this.payload.clone(),
|
||||
nonce: nonce is _idt.ByteData? ? nonce : this.nonce?.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SittingUpdateTable extends _is.UpdateTable<SittingTable> {
|
||||
SittingUpdateTable(super.table);
|
||||
|
||||
_is.ColumnValue<int, int> spaceId(int? value) => _is.ColumnValue(
|
||||
table.spaceId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> keyId(String? value) => _is.ColumnValue(
|
||||
table.keyId,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> payload(_idt.ByteData value) =>
|
||||
_is.ColumnValue(
|
||||
table.payload,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<_idt.ByteData, _idt.ByteData> nonce(_idt.ByteData? value) =>
|
||||
_is.ColumnValue(
|
||||
table.nonce,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
class SittingTable extends _is.Table<_is.UuidValue?> {
|
||||
SittingTable({super.tableRelation}) : super(tableName: 'sitting') {
|
||||
updateTable = SittingUpdateTable(this);
|
||||
spaceId = _is.ColumnInt(
|
||||
'spaceId',
|
||||
this,
|
||||
);
|
||||
keyId = _is.ColumnString(
|
||||
'keyId',
|
||||
this,
|
||||
);
|
||||
payload = _is.ColumnByteData(
|
||||
'payload',
|
||||
this,
|
||||
);
|
||||
nonce = _is.ColumnByteData(
|
||||
'nonce',
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
late final SittingUpdateTable updateTable;
|
||||
|
||||
/// The space owning this row. Maintained by the sync engine.
|
||||
late final _is.ColumnInt spaceId;
|
||||
|
||||
/// The key it is sealed with (ADR 0002's envelope); never null in Meditate,
|
||||
/// where everything is sealed.
|
||||
late final _is.ColumnString keyId;
|
||||
|
||||
/// The content, sealed: UTF-8 JSON with a `schemaVersion` once opened.
|
||||
late final _is.ColumnByteData payload;
|
||||
|
||||
/// The sealing's nonce (ADR 0002's envelope).
|
||||
late final _is.ColumnByteData nonce;
|
||||
|
||||
@override
|
||||
List<_is.Column> get columns => [
|
||||
id,
|
||||
spaceId,
|
||||
keyId,
|
||||
payload,
|
||||
nonce,
|
||||
];
|
||||
}
|
||||
|
||||
class SittingInclude extends _is.IncludeObject {
|
||||
SittingInclude._();
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => Sitting.t;
|
||||
}
|
||||
|
||||
class SittingIncludeList extends _is.IncludeList {
|
||||
SittingIncludeList._({
|
||||
_is.WhereExpressionBuilder<SittingTable>? where,
|
||||
super.limit,
|
||||
super.offset,
|
||||
super.orderBy,
|
||||
super.orderByList,
|
||||
super.include,
|
||||
}) {
|
||||
super.where = where?.call(Sitting.t);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, _is.Include?> get includes => include?.includes ?? {};
|
||||
|
||||
@override
|
||||
_is.Table<_is.UuidValue?> get table => Sitting.t;
|
||||
}
|
||||
|
||||
class SittingRepository {
|
||||
const SittingRepository._();
|
||||
|
||||
/// Returns a list of [Sitting]s matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order of the items use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// The maximum number of items can be set by [limit]. If no limit is set,
|
||||
/// all items matching the query will be returned.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which [limit] (or all)
|
||||
/// items are read from the database.
|
||||
///
|
||||
/// ```dart
|
||||
/// var persons = await Persons.db.find(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.firstName,
|
||||
/// limit: 100,
|
||||
/// );
|
||||
/// ```
|
||||
Future<List<Sitting>> find(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<SittingTable>? where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<SittingTable>? orderBy,
|
||||
_is.OrderByListBuilder<SittingTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.find<Sitting>(
|
||||
where: where?.call(Sitting.t),
|
||||
orderBy: orderBy?.call(Sitting.t),
|
||||
orderByList: orderByList?.call(Sitting.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the first matching [Sitting] matching the given query parameters.
|
||||
///
|
||||
/// Use [where] to specify which items to include in the return value.
|
||||
/// If none is specified, all items will be returned.
|
||||
///
|
||||
/// To specify the order use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// [offset] defines how many items to skip, after which the next one will be picked.
|
||||
///
|
||||
/// ```dart
|
||||
/// var youngestPerson = await Persons.db.findFirstRow(
|
||||
/// session,
|
||||
/// where: (t) => t.lastName.equals('Jones'),
|
||||
/// orderBy: (t) => t.age,
|
||||
/// );
|
||||
/// ```
|
||||
Future<Sitting?> findFirstRow(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<SittingTable>? where,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<SittingTable>? orderBy,
|
||||
_is.OrderByListBuilder<SittingTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findFirstRow<Sitting>(
|
||||
where: where?.call(Sitting.t),
|
||||
orderBy: orderBy?.call(Sitting.t),
|
||||
orderByList: orderByList?.call(Sitting.t),
|
||||
offset: offset,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds a single [Sitting] by its [id] or null if no such row exists.
|
||||
Future<Sitting?> findById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
_is.Transaction? transaction,
|
||||
_is.LockMode? lockMode,
|
||||
_is.LockBehavior? lockBehavior,
|
||||
}) async {
|
||||
return session.db.findById<Sitting>(
|
||||
id,
|
||||
transaction: transaction,
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts all [Sitting]s in the list and returns the inserted rows.
|
||||
///
|
||||
/// The returned [Sitting]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// insert, none of the rows will be inserted.
|
||||
///
|
||||
/// If [ignoreConflicts] is set to `true`, rows that conflict with existing
|
||||
/// rows are silently skipped, and only the successfully inserted rows are
|
||||
/// returned.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the inserted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<Sitting>> insert(
|
||||
_is.DatabaseSession session,
|
||||
List<Sitting> rows, {
|
||||
_is.Transaction? transaction,
|
||||
bool ignoreConflicts = false,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.insert<Sitting>(
|
||||
rows,
|
||||
transaction: transaction,
|
||||
ignoreConflicts: ignoreConflicts,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Inserts a single [Sitting] and returns the inserted row.
|
||||
///
|
||||
/// The returned [Sitting] will have its `id` field set.
|
||||
Future<Sitting> insertRow(
|
||||
_is.DatabaseSession session,
|
||||
Sitting row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.insertRow<Sitting>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts all [Sitting]s in the list and returns the resulting rows.
|
||||
///
|
||||
/// If a row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated with the new values. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies to rows matching the
|
||||
/// given expression. Conflicting rows that don't match are skipped and not
|
||||
/// returned, so the resulting list may be shorter than [rows].
|
||||
///
|
||||
/// The returned [Sitting]s will have their `id` fields set.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fails,
|
||||
/// none of the rows will be affected.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the resulting rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<Sitting>> upsert(
|
||||
_is.DatabaseSession session,
|
||||
List<Sitting> rows, {
|
||||
required _is.ColumnSelections<SittingTable> conflictColumns,
|
||||
_is.ColumnSelections<SittingTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<SittingTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.upsert<Sitting>(
|
||||
rows,
|
||||
conflictColumns: conflictColumns(Sitting.t),
|
||||
updateColumns: updateColumns?.call(Sitting.t),
|
||||
updateWhere: updateWhere?.call(Sitting.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Upserts a single [Sitting] and returns the resulting row.
|
||||
///
|
||||
/// If the row conflicts on the given [conflictColumns], the existing row is
|
||||
/// updated. Otherwise, a new row is inserted.
|
||||
///
|
||||
/// If [updateColumns] is provided, only those columns will be updated on
|
||||
/// conflict. If null, all non-conflict, non-id columns are updated.
|
||||
///
|
||||
/// If [updateWhere] is provided, the update only applies when the existing
|
||||
/// row matches the expression. Returns `null` if no row was affected — for
|
||||
/// example when [updateWhere] does not match the conflicting row.
|
||||
///
|
||||
/// The returned [Sitting] will have its `id` field set.
|
||||
Future<Sitting?> upsertRow(
|
||||
_is.DatabaseSession session,
|
||||
Sitting row, {
|
||||
required _is.ColumnSelections<SittingTable> conflictColumns,
|
||||
_is.ColumnSelections<SittingTable>? updateColumns,
|
||||
_is.WhereExpressionBuilder<SittingTable>? updateWhere,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.upsertRow<Sitting>(
|
||||
row,
|
||||
conflictColumns: conflictColumns(Sitting.t),
|
||||
updateColumns: updateColumns?.call(Sitting.t),
|
||||
updateWhere: updateWhere?.call(Sitting.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Sitting]s in the list and returns the updated rows. If
|
||||
/// [columns] is provided, only those columns will be updated. Defaults to
|
||||
/// all columns.
|
||||
/// This is an atomic operation, meaning that if one of the rows fails to
|
||||
/// update, none of the rows will be updated.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<Sitting>> update(
|
||||
_is.DatabaseSession session,
|
||||
List<Sitting> rows, {
|
||||
_is.ColumnSelections<SittingTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.update<Sitting>(
|
||||
rows,
|
||||
columns: columns?.call(Sitting.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Sitting]. The row needs to have its id set.
|
||||
/// Optionally, a list of [columns] can be provided to only update those
|
||||
/// columns. Defaults to all columns.
|
||||
Future<Sitting> updateRow(
|
||||
_is.DatabaseSession session,
|
||||
Sitting row, {
|
||||
_is.ColumnSelections<SittingTable>? columns,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateRow<Sitting>(
|
||||
row,
|
||||
columns: columns?.call(Sitting.t),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates a single [Sitting] by its [id] with the specified [columnValues].
|
||||
/// Returns the updated row or null if no row with the given id exists.
|
||||
Future<Sitting?> updateById(
|
||||
_is.DatabaseSession session,
|
||||
_is.UuidValue id, {
|
||||
required _is.ColumnValueListBuilder<SittingUpdateTable> columnValues,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.updateById<Sitting>(
|
||||
id,
|
||||
columnValues: columnValues(Sitting.t.updateTable),
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Updates all [Sitting]s matching the [where] expression with the specified [columnValues].
|
||||
/// Returns the list of updated rows.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the updated rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<Sitting>> updateWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.ColumnValueListBuilder<SittingUpdateTable> columnValues,
|
||||
required _is.WhereExpressionBuilder<SittingTable> where,
|
||||
int? limit,
|
||||
int? offset,
|
||||
_is.OrderByBuilder<SittingTable>? orderBy,
|
||||
_is.OrderByListBuilder<SittingTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.updateWhere<Sitting>(
|
||||
columnValues: columnValues(Sitting.t.updateTable),
|
||||
where: where(Sitting.t),
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: orderBy?.call(Sitting.t),
|
||||
orderByList: orderByList?.call(Sitting.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all [Sitting]s in the list and returns the deleted rows.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// This is an atomic operation, meaning that if one of the rows fail to
|
||||
/// be deleted, none of the rows will be deleted.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<Sitting>> delete(
|
||||
_is.DatabaseSession session,
|
||||
List<Sitting> rows, {
|
||||
_is.OrderByBuilder<SittingTable>? orderBy,
|
||||
_is.OrderByListBuilder<SittingTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.delete<Sitting>(
|
||||
rows,
|
||||
orderBy: orderBy?.call(Sitting.t),
|
||||
orderByList: orderByList?.call(Sitting.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes a single [Sitting].
|
||||
Future<Sitting> deleteRow(
|
||||
_is.DatabaseSession session,
|
||||
Sitting row, {
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.deleteRow<Sitting>(
|
||||
row,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Deletes all rows matching the [where] expression.
|
||||
///
|
||||
/// To specify the order of the returned rows use [orderBy] or [orderByList]
|
||||
/// when sorting by multiple columns.
|
||||
///
|
||||
/// If [noReturn] is set to `true`, the deleted rows are not read back from
|
||||
/// the database and an empty list is returned. This avoids the overhead of
|
||||
/// transferring and deserializing the rows when the result is not needed.
|
||||
Future<List<Sitting>> deleteWhere(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<SittingTable> where,
|
||||
_is.OrderByBuilder<SittingTable>? orderBy,
|
||||
_is.OrderByListBuilder<SittingTable>? orderByList,
|
||||
_is.Transaction? transaction,
|
||||
bool noReturn = false,
|
||||
}) async {
|
||||
return session.db.deleteWhere<Sitting>(
|
||||
where: where(Sitting.t),
|
||||
orderBy: orderBy?.call(Sitting.t),
|
||||
orderByList: orderByList?.call(Sitting.t),
|
||||
transaction: transaction,
|
||||
noReturn: noReturn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Counts the number of rows matching the [where] expression. If omitted,
|
||||
/// will return the count of all rows in the table.
|
||||
Future<int> count(
|
||||
_is.DatabaseSession session, {
|
||||
_is.WhereExpressionBuilder<SittingTable>? where,
|
||||
int? limit,
|
||||
_is.Transaction? transaction,
|
||||
}) async {
|
||||
return session.db.count<Sitting>(
|
||||
where: where?.call(Sitting.t),
|
||||
limit: limit,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
|
||||
/// Acquires row-level locks on [Sitting] rows matching the [where] expression.
|
||||
Future<void> lockRows(
|
||||
_is.DatabaseSession session, {
|
||||
required _is.WhereExpressionBuilder<SittingTable> where,
|
||||
required _is.LockMode lockMode,
|
||||
required _is.Transaction transaction,
|
||||
_is.LockBehavior lockBehavior = _is.LockBehavior.wait,
|
||||
}) async {
|
||||
return session.db.lockRows<Sitting>(
|
||||
where: where(Sitting.t),
|
||||
lockMode: lockMode,
|
||||
lockBehavior: lockBehavior,
|
||||
transaction: transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/* 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;
|
||||
import 'privacy/content_key.dart' as _imed3x5d;
|
||||
import 'settings/meditate_settings.dart' as _iutacwgf;
|
||||
import 'sitting/sitting.dart' as _ic8w2fbu;
|
||||
|
||||
/// The tables synchronized between client and server by the
|
||||
/// `serverpod_offline_sync` package, including the ones owned by modules and
|
||||
/// shared packages.
|
||||
final List<_is.Table> syncTables = [
|
||||
_imed3x5d.ContentKey.t,
|
||||
_iutacwgf.MeditateSettings.t,
|
||||
_ic8w2fbu.Sitting.t,
|
||||
];
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import 'package:halkyon_base_server/base.dart';
|
||||
import 'package:halkyon_meditate_server/src/meditate_data.dart';
|
||||
|
||||
/// Where this project's source is published, which the instance names
|
||||
/// unless its operator sets `SOURCE_URL`. Both licences require offering it
|
||||
/// to the people who use the instance (AGPL-3.0-or-later for the app,
|
||||
/// SSPL-1.0 for the server).
|
||||
const upstreamSourceCodeUrl =
|
||||
'https://code.halkyon.app/project-halkyon/halkyon';
|
||||
|
||||
/// The oldest build of the app this server works with (the number after `+`
|
||||
/// in the app's version); apps below it are asked to update. Raise it in the
|
||||
/// commit that breaks older apps, to the build of the first that works again.
|
||||
const minimumAppBuild = 1;
|
||||
|
||||
/// How Halkyon Meditate sets halkyon_base up (ADR 0014), the module giving every
|
||||
/// Halkyon server its sign-in, the account and its deletion, the pruning and
|
||||
/// the instance's basics: the instance says it serves `meditate` (the app accepts
|
||||
/// only such an instance; never change it), sign-in mails name the app, and
|
||||
/// the instance names this project's source. A person's sittings and keys go
|
||||
/// with them ([deleteMeditateData]). It holds the instance's
|
||||
/// register, or is a member of the one [accounts] names (ADR 0016; the
|
||||
/// server reads it from the operator's environment, `ACCOUNT_REGISTER_URL`).
|
||||
/// The server sets it up before it starts (`server.dart`), and so do its
|
||||
/// tests.
|
||||
HalkyonBase meditateBase({AccountsMode accounts = const HoldsRegister()}) =>
|
||||
HalkyonBase(
|
||||
app: 'meditate',
|
||||
appName: 'Halkyon Meditate',
|
||||
upstreamSourceCodeUrl: upstreamSourceCodeUrl,
|
||||
minimumAppBuild: minimumAppBuild,
|
||||
deletePersonData: const [deleteMeditateData],
|
||||
accounts: accounts,
|
||||
);
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import 'package:halkyon_base_server/base.dart';
|
||||
import 'package:serverpod/serverpod.dart';
|
||||
import 'package:serverpod_offline_sync_server/serverpod_offline_sync_server.dart';
|
||||
|
||||
/// What Halkyon Meditate deletes of a person who goes (ADR 0014):
|
||||
/// halkyon_base's `deletePerson` runs it in its transaction, once their
|
||||
/// sessions ended (a device with a valid session would upload everything
|
||||
/// again) and before their identities and the person go. Their personal
|
||||
/// space goes with everything in it: the foreign keys cascade into every
|
||||
/// synced table (their keys, sittings and settings, ADR 0038, D7) and the
|
||||
/// sync engine's bookkeeping.
|
||||
Future<AfterDeletion?> deleteMeditateData(
|
||||
Session session,
|
||||
UuidValue authUserId,
|
||||
Transaction transaction,
|
||||
) async {
|
||||
// DELETE FROM "offline_sync_space_members" WHERE "userUuid" = $1
|
||||
await OfflineSyncSpaceMember.db.deleteWhere(
|
||||
session,
|
||||
where: (t) => t.userUuid.equals(authUserId),
|
||||
transaction: transaction,
|
||||
);
|
||||
// The personal space is keyed on the person's id.
|
||||
// DELETE FROM "offline_sync_spaces" WHERE "uuidSpaceId" = $1
|
||||
await OfflineSyncSpace.db.deleteWhere(
|
||||
session,
|
||||
where: (t) => t.uuidSpaceId.equals(authUserId),
|
||||
transaction: transaction,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
### A data key of the person's (ADR 0038, D7, as Train's ADR 0030, D15): the
|
||||
### one their settings and sittings are sealed with. Sealed on a device
|
||||
### to the account's X25519 key and signed by it (halkyon_keys'
|
||||
### `sealDataKey`), under an ID that says what it is for, so only the
|
||||
### person's devices open it and the instance can't relabel it; the instance
|
||||
### keeps it as it is. Synced, so the key arrives with the data, works offline
|
||||
### and goes with the space. Never updated.
|
||||
class: ContentKey
|
||||
table: content_key
|
||||
database: sync
|
||||
fields:
|
||||
### The key's ID, which the rows it seals name in their `keyId`; sealed
|
||||
### with the key too, so the column is never trusted.
|
||||
dataKeyId: String
|
||||
### The sealed key: the sealer's Ed25519 signature, then the sealed box.
|
||||
sealedKey: ByteData
|
||||
indexes:
|
||||
# Deleting a space cascades into every synced table, and Postgres indexes no
|
||||
# foreign key by itself: without this, deleting a person reads the table.
|
||||
content_key_space_idx:
|
||||
fields: spaceId
|
||||
64
apps/meditate/halkyon_meditate_server/lib/src/pruning.dart
Normal file
64
apps/meditate/halkyon_meditate_server/lib/src/pruning.dart
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import 'package:halkyon_meditate_server/src/generated/future_calls.dart';
|
||||
import 'package:serverpod/serverpod.dart';
|
||||
|
||||
/// The identifier of the daily pruning of accounts.
|
||||
const _pruning = 'prune-accounts';
|
||||
|
||||
/// The identifier the daily pruning had until ADR 0020, D25.
|
||||
const _anonymousPruning = 'prune-abandoned-anonymous-people';
|
||||
|
||||
/// The identifier of the hourly forgetting of sign-up attempts.
|
||||
const _attemptPruning = 'forget-sign-up-attempts';
|
||||
|
||||
/// The identifier of the hourly telling of the members of deleted accounts.
|
||||
const _deletionNotices = 'tell-members-of-deletions';
|
||||
|
||||
/// The identifier of the daily deleting of accounts the register forgot.
|
||||
const _deletedAccounts = 'forget-deleted-accounts';
|
||||
|
||||
/// The future calls [schedulePruning] keeps scheduled, by their identifiers.
|
||||
const pruningIdentifiers = [
|
||||
_pruning,
|
||||
_attemptPruning,
|
||||
_deletionNotices,
|
||||
_deletedAccounts,
|
||||
];
|
||||
|
||||
/// Schedules the pruning (`PruningFutureCall`): the anonymous accounts left
|
||||
/// from before daily, sign-up attempts and expired email codes hourly, and the instance's
|
||||
/// accounts (ADR 0016): the register's deletions its members haven't
|
||||
/// confirmed hourly, a member's people the register forgot daily (each does
|
||||
/// nothing in the other mode). Scheduling always adds an entry, so the
|
||||
/// previous one goes first, and starting the server again leaves one of
|
||||
/// each; the one scheduled under the pruning's earlier name, which would
|
||||
/// call nothing, goes too.
|
||||
Future<void> schedulePruning(Serverpod pod) async {
|
||||
await pod.futureCalls.cancel(_anonymousPruning);
|
||||
await pod.futureCalls.cancel(_pruning);
|
||||
await pod.futureCalls
|
||||
.callRecurring(identifier: _pruning)
|
||||
.cron('17 3 * * *')
|
||||
.pruning
|
||||
.pruneAccounts();
|
||||
|
||||
await pod.futureCalls.cancel(_attemptPruning);
|
||||
await pod.futureCalls
|
||||
.callRecurring(identifier: _attemptPruning)
|
||||
.cron('27 * * * *')
|
||||
.pruning
|
||||
.forgetSignUpAttempts();
|
||||
|
||||
await pod.futureCalls.cancel(_deletionNotices);
|
||||
await pod.futureCalls
|
||||
.callRecurring(identifier: _deletionNotices)
|
||||
.cron('52 * * * *')
|
||||
.pruning
|
||||
.tellMembersOfDeletions();
|
||||
|
||||
await pod.futureCalls.cancel(_deletedAccounts);
|
||||
await pod.futureCalls
|
||||
.callRecurring(identifier: _deletedAccounts)
|
||||
.cron('37 3 * * *')
|
||||
.pruning
|
||||
.forgetDeletedAccounts();
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
import 'package:clock/clock.dart';
|
||||
import 'package:halkyon_base_server/base.dart';
|
||||
import 'package:serverpod/serverpod.dart';
|
||||
|
||||
/// Deletes the anonymous accounts left from before (ADR 0020, D25), daily,
|
||||
/// and the IP addresses of attempts the rate limits are done with and the
|
||||
/// email sign-in codes past their time, hourly: halkyon_base's pruning (ADR 0014),
|
||||
/// which the server schedules when it starts (`schedulePruning`). Then the
|
||||
/// instance's accounts (ADR 0016): as its register, telling the members
|
||||
/// again of the accounts gone, hourly; as a member, deleting the people
|
||||
/// whose account the register no longer knows, daily.
|
||||
class PruningFutureCall extends FutureCall {
|
||||
/// Prunes one batch of anonymous accounts and logs how many people went,
|
||||
/// forgets the resets that lapsed (ADR 0017) and the assistants' figures
|
||||
/// past their 3 months (Learn's ADR 0021, D41), and deletes the new accounts left
|
||||
/// unapproved for 14 days, with the waiting passes past their time
|
||||
/// (Learn's ADR 0021, D25).
|
||||
Future<void> pruneAccounts(Session session) async {
|
||||
final now = clock.now();
|
||||
final pruned = await pruneAnonymousPeople(session);
|
||||
final resets = await forgetLapsedResets(session, now: now);
|
||||
final unapproved = await forgetUnapprovedSignUps(session, now: now);
|
||||
final figures = await forgetOldAssistantFigures(session, now: now);
|
||||
session.log(
|
||||
'Pruned $pruned anonymous people, $resets lapsed resets, '
|
||||
'$unapproved accounts never approved and $figures days of assistant '
|
||||
'figures.',
|
||||
);
|
||||
}
|
||||
|
||||
/// Forgets the sign-up attempts the rate limit is done with, with their IP
|
||||
/// addresses, the email sign-in codes past their time, the password
|
||||
/// sign-ins never finished and authenticators never confirmed (ADR 0017),
|
||||
/// and the SimpleLogin sign-ins never finished (ADR 0020), and logs how
|
||||
/// many.
|
||||
Future<void> forgetSignUpAttempts(Session session) async {
|
||||
final now = clock.now();
|
||||
final forgotten = await forgetOldSignUpAttempts(session, now: now);
|
||||
final codes = await forgetExpiredEmailCodes(session, now: now);
|
||||
final unfinished = await forgetUnconfirmedAuthenticators(
|
||||
session,
|
||||
now: now,
|
||||
);
|
||||
final simpleLogins = await forgetUnfinishedSimpleLogins(session, now: now);
|
||||
session.log(
|
||||
'Forgot $forgotten sign-up attempts, $codes email codes, '
|
||||
'$unfinished unfinished sign-ins and authenticators and '
|
||||
'$simpleLogins unfinished SimpleLogin sign-ins.',
|
||||
);
|
||||
}
|
||||
|
||||
/// On the register, tells the instance's members again of the accounts
|
||||
/// gone (ADR 0016) and of the accounts whose sessions ended (ADR 0017: a
|
||||
/// password changed or reset) that they haven't confirmed yet, and logs how
|
||||
/// many confirmed.
|
||||
Future<void> tellMembersOfDeletions(Session session) async {
|
||||
final confirmed = await deliverDeletionNotices(session);
|
||||
final ended = await deliverSessionNotices(session);
|
||||
session.log(
|
||||
'Members confirmed $confirmed deleted accounts and $ended accounts '
|
||||
'whose sessions ended.',
|
||||
);
|
||||
}
|
||||
|
||||
/// On a member, deletes the people whose account the register no longer
|
||||
/// knows, and logs how many.
|
||||
Future<void> forgetDeletedAccounts(Session session) async {
|
||||
final forgotten = await forgetPeopleTheRegisterForgot(session);
|
||||
session.log('Deleted $forgotten people the register no longer knows.');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
### The person's settings of Meditate's (ADR 0038, D7, D11): one row of theirs,
|
||||
### under an ID keyed by their account, holding the bells, the screen kept on,
|
||||
### the timer's length, the favourite practices and whether the streak shows in
|
||||
### its sealed payload; the instance sees that it exists, its size and when it
|
||||
### changed. The last writer wins. Synced.
|
||||
class: MeditateSettings
|
||||
table: meditate_settings
|
||||
database: sync
|
||||
fields:
|
||||
### The key it is sealed with (ADR 0002's envelope); never null in Meditate,
|
||||
### where everything is sealed.
|
||||
keyId: String?
|
||||
### The content, sealed: UTF-8 JSON with a `schemaVersion` once opened.
|
||||
payload: ByteData
|
||||
### The sealing's nonce (ADR 0002's envelope).
|
||||
nonce: ByteData?
|
||||
indexes:
|
||||
# Deleting a space finds its rows through this index (see content_key).
|
||||
meditate_settings_space_idx:
|
||||
fields: spaceId
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
### A sitting the person kept (ADR 0038, D7): the practice or the timer, when
|
||||
### it started (a wall-clock time with the device's offset), how long it was
|
||||
### planned and how long was sat, all in its sealed payload; the instance sees
|
||||
### that it exists, its size and when it changed. Written once, deleted by the
|
||||
### person alone. Called a sitting, as Serverpod's `Session` is every
|
||||
### endpoint's. Synced.
|
||||
class: Sitting
|
||||
table: sitting
|
||||
database: sync
|
||||
fields:
|
||||
### The key it is sealed with (ADR 0002's envelope); never null in Meditate,
|
||||
### where everything is sealed.
|
||||
keyId: String?
|
||||
### The content, sealed: UTF-8 JSON with a `schemaVersion` once opened.
|
||||
payload: ByteData
|
||||
### The sealing's nonce (ADR 0002's envelope).
|
||||
nonce: ByteData?
|
||||
indexes:
|
||||
# Deleting a space finds its rows through this index (see content_key).
|
||||
sitting_space_idx:
|
||||
fields: spaceId
|
||||
Loading…
Reference in a new issue