feat(todo): the sync engine and Todo's first models (ADR 0033, D5, D6)
Todo's server syncs with serverpod_offline_sync as Learn's and Train's do, and holds two synced tables, every payload sealed on the devices: content_key (the person's data key, sealed to the account) and project (the Inbox or one of the person's own, changed when edited). A person's space goes with them, with everything in it. The migration only adds: the two tables, each indexed on spaceId and cascading with its space (ON DELETE CASCADE to offline_sync_spaces), the sync engine's own tables (crdt_*, offline_sync_*) with their foreign keys, the gen_random_uuid_v7() function their ids default to, and the modules' version rows. Nothing is dropped or changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
492c657980
commit
3876e30681
1 changed files with 28530 additions and 5 deletions
|
|
@ -18,15 +18,174 @@ import 'package:serverpod_auth_core_client/serverpod_auth_core_client.dart'
|
|||
import 'package:serverpod_auth_idp_client/serverpod_auth_idp_client.dart'
|
||||
as _iaic;
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
import 'package:serverpod_database/serverpod_database.dart' as _isd;
|
||||
import 'package:serverpod_offline_sync_client/serverpod_offline_sync_client.dart'
|
||||
as _ipulbpi2;
|
||||
import 'privacy/content_key.dart' as _imed3x5d;
|
||||
import 'project/project.dart' as _i9c44spp;
|
||||
export 'privacy/content_key.dart';
|
||||
export 'project/project.dart';
|
||||
export 'client.dart';
|
||||
export 'sync_tables.dart';
|
||||
|
||||
class Protocol extends _isc.SerializationManager {
|
||||
class Protocol extends _isd.DatabaseSerializationManager {
|
||||
Protocol._();
|
||||
|
||||
factory Protocol() => _instance;
|
||||
|
||||
static final Protocol _instance = Protocol._().._registerHostProtocols();
|
||||
|
||||
static List<_isd.TableDefinition> get targetTableDefinitions => [
|
||||
_isd.TableDefinition(
|
||||
name: 'content_key',
|
||||
dartName: 'ContentKey',
|
||||
schema: 'public',
|
||||
module: 'halkyon_todo',
|
||||
columns: [
|
||||
_isd.ColumnDefinition(
|
||||
name: 'id',
|
||||
columnType: _isd.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue?',
|
||||
columnDefault: 'random_v7',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'spaceId',
|
||||
columnType: _isd.ColumnType.bigint,
|
||||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'dataKeyId',
|
||||
columnType: _isd.ColumnType.text,
|
||||
isNullable: false,
|
||||
dartType: 'String',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'sealedKey',
|
||||
columnType: _isd.ColumnType.bytea,
|
||||
isNullable: false,
|
||||
dartType: 'dart:typed_data:ByteData',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isd.ForeignKeyDefinition(
|
||||
constraintName: 'content_key_fk_0',
|
||||
columns: ['spaceId'],
|
||||
referenceTable: 'offline_sync_spaces',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isd.ForeignKeyAction.noAction,
|
||||
onDelete: _isd.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isd.IndexDefinition(
|
||||
indexName: 'content_key_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isd.IndexElementDefinition(
|
||||
type: _isd.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
_isd.TableDefinition(
|
||||
name: 'project',
|
||||
dartName: 'Project',
|
||||
schema: 'public',
|
||||
module: 'halkyon_todo',
|
||||
columns: [
|
||||
_isd.ColumnDefinition(
|
||||
name: 'id',
|
||||
columnType: _isd.ColumnType.uuid,
|
||||
isNullable: false,
|
||||
dartType: 'UuidValue?',
|
||||
columnDefault: 'random_v7',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'spaceId',
|
||||
columnType: _isd.ColumnType.bigint,
|
||||
isNullable: true,
|
||||
dartType: 'int?',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'keyId',
|
||||
columnType: _isd.ColumnType.text,
|
||||
isNullable: true,
|
||||
dartType: 'String?',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'payload',
|
||||
columnType: _isd.ColumnType.bytea,
|
||||
isNullable: false,
|
||||
dartType: 'dart:typed_data:ByteData',
|
||||
),
|
||||
_isd.ColumnDefinition(
|
||||
name: 'nonce',
|
||||
columnType: _isd.ColumnType.bytea,
|
||||
isNullable: true,
|
||||
dartType: 'dart:typed_data:ByteData?',
|
||||
),
|
||||
],
|
||||
foreignKeys: [
|
||||
_isd.ForeignKeyDefinition(
|
||||
constraintName: 'project_fk_0',
|
||||
columns: ['spaceId'],
|
||||
referenceTable: 'offline_sync_spaces',
|
||||
referenceTableSchema: 'public',
|
||||
referenceColumns: ['id'],
|
||||
onUpdate: _isd.ForeignKeyAction.noAction,
|
||||
onDelete: _isd.ForeignKeyAction.cascade,
|
||||
matchType: null,
|
||||
),
|
||||
],
|
||||
indexes: [
|
||||
_isd.IndexDefinition(
|
||||
indexName: 'project_space_idx',
|
||||
tableSpace: null,
|
||||
elements: [
|
||||
_isd.IndexElementDefinition(
|
||||
type: _isd.IndexElementDefinitionType.column,
|
||||
definition: 'spaceId',
|
||||
),
|
||||
],
|
||||
type: 'btree',
|
||||
isUnique: false,
|
||||
isPrimary: false,
|
||||
),
|
||||
],
|
||||
managed: true,
|
||||
),
|
||||
..._ihk4p5t7.Protocol() is _isd.DatabaseSerializationManager
|
||||
? (_ihk4p5t7.Protocol() as _isd.DatabaseSerializationManager)
|
||||
.getTargetTableDefinitions()
|
||||
: [],
|
||||
..._iaic.Protocol() is _isd.DatabaseSerializationManager
|
||||
? (_iaic.Protocol() as _isd.DatabaseSerializationManager)
|
||||
.getTargetTableDefinitions()
|
||||
: [],
|
||||
..._ipulbpi2.Protocol() is _isd.DatabaseSerializationManager
|
||||
? (_ipulbpi2.Protocol() as _isd.DatabaseSerializationManager)
|
||||
.getTargetTableDefinitions()
|
||||
: [],
|
||||
..._ibkpwzgl.Protocol() is _isd.DatabaseSerializationManager
|
||||
? (_ibkpwzgl.Protocol() as _isd.DatabaseSerializationManager)
|
||||
.getTargetTableDefinitions()
|
||||
: [],
|
||||
..._iacc.Protocol() is _isd.DatabaseSerializationManager
|
||||
? (_iacc.Protocol() as _isd.DatabaseSerializationManager)
|
||||
.getTargetTableDefinitions()
|
||||
: [],
|
||||
];
|
||||
|
||||
static String? getClassNameFromObjectJson(dynamic data) {
|
||||
if (data is! Map) return null;
|
||||
final className = data['__className__'] as String?;
|
||||
|
|
@ -54,12 +213,27 @@ class Protocol extends _isc.SerializationManager {
|
|||
}
|
||||
}
|
||||
|
||||
if (t == _imed3x5d.ContentKey) {
|
||||
return _imed3x5d.ContentKey.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i9c44spp.Project) {
|
||||
return _i9c44spp.Project.fromJson(data) as T;
|
||||
}
|
||||
if (t == _isc.getType<_imed3x5d.ContentKey?>()) {
|
||||
return (data != null ? _imed3x5d.ContentKey.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _isc.getType<_i9c44spp.Project?>()) {
|
||||
return (data != null ? _i9c44spp.Project.fromJson(data) : null) as T;
|
||||
}
|
||||
try {
|
||||
return _ihk4p5t7.Protocol().deserialize<T>(data, t);
|
||||
} on _isc.DeserializationTypeNotFoundException catch (_) {}
|
||||
try {
|
||||
return _iaic.Protocol().deserialize<T>(data, t);
|
||||
} on _isc.DeserializationTypeNotFoundException catch (_) {}
|
||||
try {
|
||||
return _ipulbpi2.Protocol().deserialize<T>(data, t);
|
||||
} on _isc.DeserializationTypeNotFoundException catch (_) {}
|
||||
try {
|
||||
return _ibkpwzgl.Protocol().deserialize<T>(data, t);
|
||||
} on _isc.DeserializationTypeNotFoundException catch (_) {}
|
||||
|
|
@ -71,6 +245,8 @@ class Protocol extends _isc.SerializationManager {
|
|||
|
||||
static String? getClassNameForType(Type type) {
|
||||
return switch (type) {
|
||||
_imed3x5d.ContentKey => 'ContentKey',
|
||||
_i9c44spp.Project => 'Project',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
|
@ -87,6 +263,12 @@ class Protocol extends _isc.SerializationManager {
|
|||
);
|
||||
}
|
||||
|
||||
switch (data) {
|
||||
case _imed3x5d.ContentKey():
|
||||
return 'ContentKey';
|
||||
case _i9c44spp.Project():
|
||||
return 'Project';
|
||||
}
|
||||
className = _ihk4p5t7.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
return className.contains('.') ? className : 'halkyon_base.$className';
|
||||
|
|
@ -97,6 +279,12 @@ class Protocol extends _isc.SerializationManager {
|
|||
? className
|
||||
: 'serverpod_auth_idp.$className';
|
||||
}
|
||||
className = _ipulbpi2.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
return className.contains('.')
|
||||
? className
|
||||
: 'serverpod_offline_sync.$className';
|
||||
}
|
||||
className = _ibkpwzgl.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
return className.contains('.') ? className : 'halkyon_oidc.$className';
|
||||
|
|
@ -116,6 +304,12 @@ class Protocol extends _isc.SerializationManager {
|
|||
if (dataClassName is! String) {
|
||||
return super.deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName == 'ContentKey') {
|
||||
return deserialize<_imed3x5d.ContentKey>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'Project') {
|
||||
return deserialize<_i9c44spp.Project>(data['data']);
|
||||
}
|
||||
if (dataClassName.startsWith('halkyon_base.')) {
|
||||
data['className'] = dataClassName.substring(13);
|
||||
return _ihk4p5t7.Protocol().deserializeByClassName(data);
|
||||
|
|
@ -124,6 +318,10 @@ class Protocol extends _isc.SerializationManager {
|
|||
data['className'] = dataClassName.substring(19);
|
||||
return _iaic.Protocol().deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName.startsWith('serverpod_offline_sync.')) {
|
||||
data['className'] = dataClassName.substring(23);
|
||||
return _ipulbpi2.Protocol().deserializeByClassName(data);
|
||||
}
|
||||
if (dataClassName.startsWith('halkyon_oidc.')) {
|
||||
data['className'] = dataClassName.substring(13);
|
||||
return _ibkpwzgl.Protocol().deserializeByClassName(data);
|
||||
|
|
@ -138,10 +336,71 @@ class Protocol extends _isc.SerializationManager {
|
|||
void _registerHostProtocols() {
|
||||
_ihk4p5t7.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_iaic.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_ipulbpi2.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_ibkpwzgl.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
_iacc.Protocol().registerHostProtocol('halkyon_todo', this);
|
||||
}
|
||||
|
||||
@override
|
||||
_isd.Table? getTableForType(Type t) {
|
||||
{
|
||||
var protocol = _ihk4p5t7.Protocol();
|
||||
var table = protocol is _isd.DatabaseSerializationManager
|
||||
? (protocol as _isd.DatabaseSerializationManager).getTableForType(t)
|
||||
: null;
|
||||
if (table != null) {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
{
|
||||
var protocol = _iaic.Protocol();
|
||||
var table = protocol is _isd.DatabaseSerializationManager
|
||||
? (protocol as _isd.DatabaseSerializationManager).getTableForType(t)
|
||||
: null;
|
||||
if (table != null) {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
{
|
||||
var protocol = _ipulbpi2.Protocol();
|
||||
var table = protocol is _isd.DatabaseSerializationManager
|
||||
? (protocol as _isd.DatabaseSerializationManager).getTableForType(t)
|
||||
: null;
|
||||
if (table != null) {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
{
|
||||
var protocol = _ibkpwzgl.Protocol();
|
||||
var table = protocol is _isd.DatabaseSerializationManager
|
||||
? (protocol as _isd.DatabaseSerializationManager).getTableForType(t)
|
||||
: null;
|
||||
if (table != null) {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
{
|
||||
var protocol = _iacc.Protocol();
|
||||
var table = protocol is _isd.DatabaseSerializationManager
|
||||
? (protocol as _isd.DatabaseSerializationManager).getTableForType(t)
|
||||
: null;
|
||||
if (table != null) {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
switch (t) {
|
||||
case _imed3x5d.ContentKey:
|
||||
return _imed3x5d.ContentKey.t;
|
||||
case _i9c44spp.Project:
|
||||
return _i9c44spp.Project.t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
List<_isd.TableDefinition> getTargetTableDefinitions() =>
|
||||
targetTableDefinitions;
|
||||
|
||||
@override
|
||||
String getModuleName() => 'halkyon_todo';
|
||||
|
||||
|
|
@ -160,6 +419,9 @@ class Protocol extends _isc.SerializationManager {
|
|||
try {
|
||||
return _iaic.Protocol().mapRecordToJson(record);
|
||||
} catch (_) {}
|
||||
try {
|
||||
return _ipulbpi2.Protocol().mapRecordToJson(record);
|
||||
} catch (_) {}
|
||||
try {
|
||||
return _ibkpwzgl.Protocol().mapRecordToJson(record);
|
||||
} catch (_) {}
|
||||
|
|
|
|||
Loading…
Reference in a new issue