feat: studysets record when they last changed; the library lists them so
Decided 2026-10-01: after the starred ones, the library lists the most recently changed studysets first. That needs a change date, so studyset gains updatedAt (ADR 0004): when the studyset itself last changed, its content, place, star or archive date, written by the device making the change in the same update as the change; the last writer wins. Studysets from before have none and come last, the newest of them first. The branch's migration is still unreleased (not merged, not deployed), so it is recreated rather than followed by a second one: 20261001064926911-tree-tags-assets replaces 20260930213716558 and differs only by one line on the server, ALTER TABLE "studyset" ADD COLUMN "updatedAt" timestamp without time zone (nullable, so existing rows keep null and no data changes), the same column in the table devices rebuild, and its version. The model's comment on the payload no longer lists schema 1's fields alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
8e0f5cf538
commit
85c028fe3c
9 changed files with 349 additions and 74 deletions
|
|
@ -691,6 +691,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
isNullable: true,
|
||||
dartType: 'DateTime?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'updatedAt',
|
||||
columnType: _isp.ColumnType.timestampWithoutTimeZone,
|
||||
isNullable: true,
|
||||
dartType: 'DateTime?',
|
||||
),
|
||||
_isp.ColumnDefinition(
|
||||
name: 'keyId',
|
||||
columnType: _isp.ColumnType.text,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ abstract class Studyset
|
|||
String? position,
|
||||
this.archivedAt,
|
||||
this.starredAt,
|
||||
this.updatedAt,
|
||||
this.keyId,
|
||||
required this.payload,
|
||||
this.nonce,
|
||||
|
|
@ -45,6 +46,7 @@ abstract class Studyset
|
|||
String? position,
|
||||
DateTime? archivedAt,
|
||||
DateTime? starredAt,
|
||||
DateTime? updatedAt,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
|
|
@ -71,6 +73,9 @@ abstract class Studyset
|
|||
starredAt: jsonSerialization['starredAt'] == null
|
||||
? null
|
||||
: _is.DateTimeJsonExtension.fromJson(jsonSerialization['starredAt']),
|
||||
updatedAt: jsonSerialization['updatedAt'] == null
|
||||
? null
|
||||
: _is.DateTimeJsonExtension.fromJson(jsonSerialization['updatedAt']),
|
||||
keyId: jsonSerialization['keyId'] as String?,
|
||||
payload: _is.ByteDataJsonExtension.fromJson(jsonSerialization['payload']),
|
||||
nonce: jsonSerialization['nonce'] == null
|
||||
|
|
@ -112,11 +117,18 @@ abstract class Studyset
|
|||
/// not starred. A column of its own, like archivedAt.
|
||||
DateTime? starredAt;
|
||||
|
||||
/// When the studyset itself last changed (its content, place, star or archive
|
||||
/// date), set by the device making the change, with it; the last writer wins.
|
||||
/// Null for studysets from before this column. The library lists the most
|
||||
/// recently changed first (decided 2026-10-01).
|
||||
DateTime? updatedAt;
|
||||
|
||||
/// The encryption key's id; always null, since content isn't end-to-end encrypted
|
||||
/// (ADR 0002). Kept so opt-in encryption of single studysets stays possible.
|
||||
String? keyId;
|
||||
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", "title", "description"}.
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", "title", "description", …}; the
|
||||
/// app's studyset_payload.dart describes each schema.
|
||||
_idt.ByteData payload;
|
||||
|
||||
/// The encryption nonce; always null, like keyId.
|
||||
|
|
@ -136,6 +148,7 @@ abstract class Studyset
|
|||
String? position,
|
||||
DateTime? archivedAt,
|
||||
DateTime? starredAt,
|
||||
DateTime? updatedAt,
|
||||
String? keyId,
|
||||
_idt.ByteData? payload,
|
||||
_idt.ByteData? nonce,
|
||||
|
|
@ -151,6 +164,7 @@ abstract class Studyset
|
|||
'position': position,
|
||||
if (archivedAt != null) 'archivedAt': archivedAt?.toJson(),
|
||||
if (starredAt != null) 'starredAt': starredAt?.toJson(),
|
||||
if (updatedAt != null) 'updatedAt': updatedAt?.toJson(),
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
|
|
@ -168,6 +182,7 @@ abstract class Studyset
|
|||
'position': position,
|
||||
if (archivedAt != null) 'archivedAt': archivedAt?.toJson(),
|
||||
if (starredAt != null) 'starredAt': starredAt?.toJson(),
|
||||
if (updatedAt != null) 'updatedAt': updatedAt?.toJson(),
|
||||
if (keyId != null) 'keyId': keyId,
|
||||
'payload': payload.toJson(),
|
||||
if (nonce != null) 'nonce': nonce?.toJson(),
|
||||
|
|
@ -213,6 +228,7 @@ class _StudysetImpl extends Studyset {
|
|||
String? position,
|
||||
DateTime? archivedAt,
|
||||
DateTime? starredAt,
|
||||
DateTime? updatedAt,
|
||||
String? keyId,
|
||||
required _idt.ByteData payload,
|
||||
_idt.ByteData? nonce,
|
||||
|
|
@ -224,6 +240,7 @@ class _StudysetImpl extends Studyset {
|
|||
position: position,
|
||||
archivedAt: archivedAt,
|
||||
starredAt: starredAt,
|
||||
updatedAt: updatedAt,
|
||||
keyId: keyId,
|
||||
payload: payload,
|
||||
nonce: nonce,
|
||||
|
|
@ -241,6 +258,7 @@ class _StudysetImpl extends Studyset {
|
|||
String? position,
|
||||
Object? archivedAt = _Undefined,
|
||||
Object? starredAt = _Undefined,
|
||||
Object? updatedAt = _Undefined,
|
||||
Object? keyId = _Undefined,
|
||||
_idt.ByteData? payload,
|
||||
Object? nonce = _Undefined,
|
||||
|
|
@ -253,6 +271,7 @@ class _StudysetImpl extends Studyset {
|
|||
position: position ?? this.position,
|
||||
archivedAt: archivedAt is DateTime? ? archivedAt : this.archivedAt,
|
||||
starredAt: starredAt is DateTime? ? starredAt : this.starredAt,
|
||||
updatedAt: updatedAt is DateTime? ? updatedAt : this.updatedAt,
|
||||
keyId: keyId is String? ? keyId : this.keyId,
|
||||
payload: payload ?? this.payload.clone(),
|
||||
nonce: nonce is _idt.ByteData? ? nonce : this.nonce?.clone(),
|
||||
|
|
@ -292,6 +311,12 @@ class StudysetUpdateTable extends _is.UpdateTable<StudysetTable> {
|
|||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<DateTime, DateTime> updatedAt(DateTime? value) =>
|
||||
_is.ColumnValue(
|
||||
table.updatedAt,
|
||||
value,
|
||||
);
|
||||
|
||||
_is.ColumnValue<String, String> keyId(String? value) => _is.ColumnValue(
|
||||
table.keyId,
|
||||
value,
|
||||
|
|
@ -334,6 +359,10 @@ class StudysetTable extends _is.Table<_is.UuidValue?> {
|
|||
'starredAt',
|
||||
this,
|
||||
);
|
||||
updatedAt = _is.ColumnDateTime(
|
||||
'updatedAt',
|
||||
this,
|
||||
);
|
||||
keyId = _is.ColumnString(
|
||||
'keyId',
|
||||
this,
|
||||
|
|
@ -376,11 +405,18 @@ class StudysetTable extends _is.Table<_is.UuidValue?> {
|
|||
/// not starred. A column of its own, like archivedAt.
|
||||
late final _is.ColumnDateTime starredAt;
|
||||
|
||||
/// When the studyset itself last changed (its content, place, star or archive
|
||||
/// date), set by the device making the change, with it; the last writer wins.
|
||||
/// Null for studysets from before this column. The library lists the most
|
||||
/// recently changed first (decided 2026-10-01).
|
||||
late final _is.ColumnDateTime updatedAt;
|
||||
|
||||
/// The encryption key's id; always null, since content isn't end-to-end encrypted
|
||||
/// (ADR 0002). Kept so opt-in encryption of single studysets stays possible.
|
||||
late final _is.ColumnString keyId;
|
||||
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", "title", "description"}.
|
||||
/// The content as UTF-8 JSON: {"schemaVersion", "title", "description", …}; the
|
||||
/// app's studyset_payload.dart describes each schema.
|
||||
late final _is.ColumnByteData payload;
|
||||
|
||||
/// The encryption nonce; always null, like keyId.
|
||||
|
|
@ -407,6 +443,7 @@ class StudysetTable extends _is.Table<_is.UuidValue?> {
|
|||
position,
|
||||
archivedAt,
|
||||
starredAt,
|
||||
updatedAt,
|
||||
keyId,
|
||||
payload,
|
||||
nonce,
|
||||
|
|
|
|||
|
|
@ -25,10 +25,16 @@ fields:
|
|||
### When the person starred it, to keep it at the top of the library; null when
|
||||
### not starred. A column of its own, like archivedAt.
|
||||
starredAt: DateTime?
|
||||
### When the studyset itself last changed (its content, place, star or archive
|
||||
### date), set by the device making the change, with it; the last writer wins.
|
||||
### Null for studysets from before this column. The library lists the most
|
||||
### recently changed first (decided 2026-10-01).
|
||||
updatedAt: DateTime?
|
||||
### The encryption key's id; always null, since content isn't end-to-end encrypted
|
||||
### (ADR 0002). Kept so opt-in encryption of single studysets stays possible.
|
||||
keyId: String?
|
||||
### The content as UTF-8 JSON: {"schemaVersion", "title", "description"}.
|
||||
### The content as UTF-8 JSON: {"schemaVersion", "title", "description", …}; the
|
||||
### app's studyset_payload.dart describes each schema.
|
||||
payload: ByteData
|
||||
### The encryption nonce; always null, like keyId.
|
||||
nonce: ByteData?
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,570 @@
|
|||
{
|
||||
"__className__": "serverpod.DatabaseMigration",
|
||||
"actions": [
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "createTable",
|
||||
"createTable": {
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "asset",
|
||||
"dartName": "Asset",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "sha256",
|
||||
"fieldName": "sha256",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "mimeType",
|
||||
"fieldName": "mimeType",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "byteSize",
|
||||
"fieldName": "byteSize",
|
||||
"columnType": 6,
|
||||
"isNullable": false,
|
||||
"dartType": "int"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "keyId",
|
||||
"fieldName": "keyId",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "payload",
|
||||
"fieldName": "payload",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "nonce",
|
||||
"fieldName": "nonce",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "asset_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "asset_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "asset_hash_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "sha256"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "createTable",
|
||||
"createTable": {
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "card_tag",
|
||||
"dartName": "CardTag",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "cardId",
|
||||
"fieldName": "cardId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "tagId",
|
||||
"fieldName": "tagId",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"dartType": "UuidValue"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_tag_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_tag_fk_1",
|
||||
"columns": [
|
||||
"cardId"
|
||||
],
|
||||
"referenceTable": "card",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "card_tag_fk_2",
|
||||
"columns": [
|
||||
"tagId"
|
||||
],
|
||||
"referenceTable": "tag",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_tag_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_tag_card_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "cardId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "card_tag_tag_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "tagId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "createTable",
|
||||
"createTable": {
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "person_settings",
|
||||
"dartName": "PersonSettings",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "keyId",
|
||||
"fieldName": "keyId",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "payload",
|
||||
"fieldName": "payload",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "nonce",
|
||||
"fieldName": "nonce",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "person_settings_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "person_settings_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "alterTable",
|
||||
"alterTable": {
|
||||
"__className__": "serverpod.TableMigration",
|
||||
"name": "review_log",
|
||||
"schema": "public",
|
||||
"addColumns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "mode",
|
||||
"fieldName": "mode",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"columnDefault": "'review'",
|
||||
"dartType": "String"
|
||||
}
|
||||
],
|
||||
"deleteColumns": [],
|
||||
"modifyColumns": [],
|
||||
"addIndexes": [],
|
||||
"deleteIndexes": [],
|
||||
"addForeignKeys": [],
|
||||
"deleteForeignKeys": [],
|
||||
"warnings": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "alterTable",
|
||||
"alterTable": {
|
||||
"__className__": "serverpod.TableMigration",
|
||||
"name": "studyset",
|
||||
"schema": "public",
|
||||
"addColumns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "parentId",
|
||||
"fieldName": "parentId",
|
||||
"columnType": 7,
|
||||
"isNullable": true,
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "position",
|
||||
"fieldName": "position",
|
||||
"columnType": 0,
|
||||
"isNullable": false,
|
||||
"columnDefault": "'a0'",
|
||||
"dartType": "String"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "archivedAt",
|
||||
"fieldName": "archivedAt",
|
||||
"columnType": 4,
|
||||
"isNullable": true,
|
||||
"dartType": "DateTime?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "starredAt",
|
||||
"fieldName": "starredAt",
|
||||
"columnType": 4,
|
||||
"isNullable": true,
|
||||
"dartType": "DateTime?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "updatedAt",
|
||||
"fieldName": "updatedAt",
|
||||
"columnType": 4,
|
||||
"isNullable": true,
|
||||
"dartType": "DateTime?"
|
||||
}
|
||||
],
|
||||
"deleteColumns": [],
|
||||
"modifyColumns": [],
|
||||
"addIndexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "studyset_parent_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "parentId"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "position"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"deleteIndexes": [],
|
||||
"addForeignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "studyset_fk_1",
|
||||
"columns": [
|
||||
"parentId"
|
||||
],
|
||||
"referenceTable": "studyset",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4,
|
||||
"deferrable": "initiallyDeferred"
|
||||
}
|
||||
],
|
||||
"deleteForeignKeys": [],
|
||||
"warnings": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.DatabaseMigrationAction",
|
||||
"type": "createTable",
|
||||
"createTable": {
|
||||
"__className__": "serverpod.TableDefinition",
|
||||
"name": "tag",
|
||||
"dartName": "Tag",
|
||||
"module": "halkyon_learn",
|
||||
"schema": "public",
|
||||
"columns": [
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "id",
|
||||
"fieldName": "id",
|
||||
"columnType": 7,
|
||||
"isNullable": false,
|
||||
"columnDefault": "random_v7",
|
||||
"dartType": "UuidValue?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "spaceId",
|
||||
"fieldName": "spaceId",
|
||||
"columnType": 6,
|
||||
"isNullable": true,
|
||||
"dartType": "int?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "keyId",
|
||||
"fieldName": "keyId",
|
||||
"columnType": 0,
|
||||
"isNullable": true,
|
||||
"dartType": "String?"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "payload",
|
||||
"fieldName": "payload",
|
||||
"columnType": 5,
|
||||
"isNullable": false,
|
||||
"dartType": "dart:typed_data:ByteData"
|
||||
},
|
||||
{
|
||||
"__className__": "serverpod.ColumnDefinition",
|
||||
"name": "nonce",
|
||||
"fieldName": "nonce",
|
||||
"columnType": 5,
|
||||
"isNullable": true,
|
||||
"dartType": "dart:typed_data:ByteData?"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"__className__": "serverpod.ForeignKeyDefinition",
|
||||
"constraintName": "tag_fk_0",
|
||||
"columns": [
|
||||
"spaceId"
|
||||
],
|
||||
"referenceTable": "offline_sync_spaces",
|
||||
"referenceTableSchema": "public",
|
||||
"referenceColumns": [
|
||||
"id"
|
||||
],
|
||||
"onUpdate": 3,
|
||||
"onDelete": 4
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
{
|
||||
"__className__": "serverpod.IndexDefinition",
|
||||
"indexName": "tag_space_idx",
|
||||
"elements": [
|
||||
{
|
||||
"__className__": "serverpod.IndexElementDefinition",
|
||||
"type": 0,
|
||||
"definition": "spaceId"
|
||||
}
|
||||
],
|
||||
"type": "btree",
|
||||
"isUnique": false,
|
||||
"isPrimary": false
|
||||
}
|
||||
],
|
||||
"managed": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"warnings": [],
|
||||
"migrationApiVersion": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
BEGIN;
|
||||
|
||||
--
|
||||
-- Function: gen_random_uuid_v7()
|
||||
-- Source: https://gist.github.com/kjmph/5bd772b2c2df145aa645b837da7eca74
|
||||
-- License: MIT (copyright notice included on the generator source code).
|
||||
--
|
||||
create or replace function gen_random_uuid_v7()
|
||||
returns uuid
|
||||
as $$
|
||||
begin
|
||||
-- use random v4 uuid as starting point (which has the same variant we need)
|
||||
-- then overlay timestamp
|
||||
-- then set version 7 by flipping the 2 and 1 bit in the version 4 string
|
||||
return encode(
|
||||
set_bit(
|
||||
set_bit(
|
||||
overlay(uuid_send(gen_random_uuid())
|
||||
placing substring(int8send(floor(extract(epoch from clock_timestamp()) * 1000)::bigint) from 3)
|
||||
from 1 for 6
|
||||
),
|
||||
52, 1
|
||||
),
|
||||
53, 1
|
||||
),
|
||||
'hex')::uuid;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
volatile;
|
||||
|
||||
--
|
||||
-- ACTION CREATE TABLE
|
||||
--
|
||||
CREATE TABLE "asset" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"spaceId" bigint,
|
||||
"sha256" text NOT NULL,
|
||||
"mimeType" text NOT NULL,
|
||||
"byteSize" bigint NOT NULL,
|
||||
"keyId" text,
|
||||
"payload" bytea NOT NULL,
|
||||
"nonce" bytea
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "asset_space_idx" ON "asset" USING btree ("spaceId");
|
||||
CREATE INDEX "asset_hash_idx" ON "asset" USING btree ("spaceId", "sha256");
|
||||
|
||||
--
|
||||
-- ACTION CREATE TABLE
|
||||
--
|
||||
CREATE TABLE "card_tag" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"spaceId" bigint,
|
||||
"cardId" uuid NOT NULL,
|
||||
"tagId" uuid NOT NULL
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "card_tag_space_idx" ON "card_tag" USING btree ("spaceId");
|
||||
CREATE INDEX "card_tag_card_idx" ON "card_tag" USING btree ("cardId");
|
||||
CREATE INDEX "card_tag_tag_idx" ON "card_tag" USING btree ("tagId");
|
||||
|
||||
--
|
||||
-- ACTION CREATE TABLE
|
||||
--
|
||||
CREATE TABLE "person_settings" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"spaceId" bigint,
|
||||
"keyId" text,
|
||||
"payload" bytea NOT NULL,
|
||||
"nonce" bytea
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "person_settings_space_idx" ON "person_settings" USING btree ("spaceId");
|
||||
|
||||
--
|
||||
-- ACTION ALTER TABLE
|
||||
--
|
||||
ALTER TABLE "review_log" ADD COLUMN "mode" text NOT NULL DEFAULT 'review'::text;
|
||||
--
|
||||
-- ACTION ALTER TABLE
|
||||
--
|
||||
ALTER TABLE "studyset" ADD COLUMN "parentId" uuid;
|
||||
ALTER TABLE "studyset" ADD COLUMN "position" text NOT NULL DEFAULT 'a0'::text;
|
||||
ALTER TABLE "studyset" ADD COLUMN "archivedAt" timestamp without time zone;
|
||||
ALTER TABLE "studyset" ADD COLUMN "starredAt" timestamp without time zone;
|
||||
ALTER TABLE "studyset" ADD COLUMN "updatedAt" timestamp without time zone;
|
||||
CREATE INDEX "studyset_parent_idx" ON "studyset" USING btree ("parentId", "position");
|
||||
--
|
||||
-- ACTION CREATE TABLE
|
||||
--
|
||||
CREATE TABLE "tag" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid_v7(),
|
||||
"spaceId" bigint,
|
||||
"keyId" text,
|
||||
"payload" bytea NOT NULL,
|
||||
"nonce" bytea
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX "tag_space_idx" ON "tag" USING btree ("spaceId");
|
||||
|
||||
--
|
||||
-- ACTION CREATE FOREIGN KEY
|
||||
--
|
||||
ALTER TABLE ONLY "asset"
|
||||
ADD CONSTRAINT "asset_fk_0"
|
||||
FOREIGN KEY("spaceId")
|
||||
REFERENCES "offline_sync_spaces"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- ACTION CREATE FOREIGN KEY
|
||||
--
|
||||
ALTER TABLE ONLY "card_tag"
|
||||
ADD CONSTRAINT "card_tag_fk_0"
|
||||
FOREIGN KEY("spaceId")
|
||||
REFERENCES "offline_sync_spaces"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
ALTER TABLE ONLY "card_tag"
|
||||
ADD CONSTRAINT "card_tag_fk_1"
|
||||
FOREIGN KEY("cardId")
|
||||
REFERENCES "card"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
ALTER TABLE ONLY "card_tag"
|
||||
ADD CONSTRAINT "card_tag_fk_2"
|
||||
FOREIGN KEY("tagId")
|
||||
REFERENCES "tag"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
--
|
||||
-- ACTION CREATE FOREIGN KEY
|
||||
--
|
||||
ALTER TABLE ONLY "person_settings"
|
||||
ADD CONSTRAINT "person_settings_fk_0"
|
||||
FOREIGN KEY("spaceId")
|
||||
REFERENCES "offline_sync_spaces"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
--
|
||||
-- ACTION CREATE FOREIGN KEY
|
||||
--
|
||||
ALTER TABLE ONLY "studyset"
|
||||
ADD CONSTRAINT "studyset_fk_1"
|
||||
FOREIGN KEY("parentId")
|
||||
REFERENCES "studyset"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
--
|
||||
-- ACTION CREATE FOREIGN KEY
|
||||
--
|
||||
ALTER TABLE ONLY "tag"
|
||||
ADD CONSTRAINT "tag_fk_0"
|
||||
FOREIGN KEY("spaceId")
|
||||
REFERENCES "offline_sync_spaces"("id")
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE NO ACTION;
|
||||
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR halkyon_learn
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('halkyon_learn', '20261001064926911-tree-tags-assets', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20261001064926911-tree-tags-assets', "timestamp" = now();
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR serverpod
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('serverpod', '20260824182259319', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20260824182259319', "timestamp" = now();
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR serverpod_auth_idp
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('serverpod_auth_idp', '20260924105404509', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20260924105404509', "timestamp" = now();
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR serverpod_offline_sync
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('serverpod_offline_sync', '20260914143806119', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20260914143806119', "timestamp" = now();
|
||||
|
||||
--
|
||||
-- MIGRATION VERSION FOR serverpod_auth_core
|
||||
--
|
||||
INSERT INTO "serverpod_migrations" ("module", "version", "timestamp")
|
||||
VALUES ('serverpod_auth_core', '20260924105232991', now())
|
||||
ON CONFLICT ("module")
|
||||
DO UPDATE SET "version" = '20260924105232991', "timestamp" = now();
|
||||
|
||||
|
||||
COMMIT;
|
||||
|
|
@ -8,4 +8,4 @@
|
|||
20260930094729362-studysets
|
||||
20260930122235111-cards
|
||||
20260930145136285-reviews
|
||||
20260930213716558-tree-tags-assets
|
||||
20261001064926911-tree-tags-assets
|
||||
|
|
|
|||
Loading…
Reference in a new issue