Scaffold backend gateway and integration docs

This commit is contained in:
Rijad Zuzo
2026-02-14 20:10:16 +01:00
commit 577c4b33b7
166 changed files with 13382 additions and 0 deletions
@@ -0,0 +1,290 @@
// ignore_for_file: invalid_annotation_target
import 'package:freezed_annotation/freezed_annotation.dart';
part 'backend_models.freezed.dart';
part 'backend_models.g.dart';
/// Supported authentication methods.
@JsonEnum(fieldRename: FieldRename.snake)
enum SignInMethod {
/// Email link based sign-in.
emailMagicLink,
/// Email/password sign-in.
password,
/// OIDC token based sign-in.
oidc,
}
/// Allowed change operation types.
@JsonEnum(alwaysCreate: true)
enum ChangeOperation {
/// Create or update entity state.
@JsonValue('upsert')
upsert,
/// Remove entity state.
@JsonValue('delete')
delete,
}
/// Action taken by user against a signal item.
@JsonEnum(alwaysCreate: true)
enum SignalAction {
/// Item was viewed.
viewed,
/// Item was dismissed.
dismissed,
/// Item was saved.
saved,
/// Item resulted in purchase.
purchased,
}
/// Upload category.
@JsonEnum(alwaysCreate: true)
enum UploadPurpose {
/// Avatar upload.
@JsonValue('avatar')
avatar,
/// Generic attachment upload.
@JsonValue('attachment')
attachment,
}
/// Request body for sign-in.
@freezed
abstract class SignInRequest with _$SignInRequest {
const factory SignInRequest({
required SignInMethod method,
String? email,
String? password,
String? idToken,
}) = _SignInRequest;
factory SignInRequest.fromJson(Map<String, dynamic> json) =>
_$SignInRequestFromJson(json);
}
/// User profile returned by backend.
@freezed
abstract class UserProfile with _$UserProfile {
const factory UserProfile({
required String id,
String? email,
String? displayName,
String? avatarFileId,
}) = _UserProfile;
factory UserProfile.fromJson(Map<String, dynamic> json) =>
_$UserProfileFromJson(json);
}
/// Auth session bundle used by gateway and token storage.
@freezed
abstract class AuthSession with _$AuthSession {
@JsonSerializable(explicitToJson: true)
const factory AuthSession({
required String accessToken,
required String refreshToken,
required DateTime expiresAt,
required UserProfile user,
}) = _AuthSession;
factory AuthSession.fromJson(Map<String, dynamic> json) =>
_$AuthSessionFromJson(json);
}
/// Refresh token request body.
@freezed
abstract class AuthRefreshRequest with _$AuthRefreshRequest {
const factory AuthRefreshRequest({required String refreshToken}) =
_AuthRefreshRequest;
factory AuthRefreshRequest.fromJson(Map<String, dynamic> json) =>
_$AuthRefreshRequestFromJson(json);
}
/// Refresh token response body.
@freezed
abstract class AuthRefreshResponse with _$AuthRefreshResponse {
const factory AuthRefreshResponse({
required String accessToken,
String? refreshToken,
required DateTime expiresAt,
}) = _AuthRefreshResponse;
factory AuthRefreshResponse.fromJson(Map<String, dynamic> json) =>
_$AuthRefreshResponseFromJson(json);
}
/// Generic envelope for offline-first change operations.
@freezed
abstract class ChangeEnvelope with _$ChangeEnvelope {
const factory ChangeEnvelope({
required int schemaVersion,
required String entityType,
required String entityId,
required ChangeOperation op,
required DateTime modifiedAt,
required String clientMutationId,
Map<String, dynamic>? payload,
}) = _ChangeEnvelope;
factory ChangeEnvelope.fromJson(Map<String, dynamic> json) =>
_$ChangeEnvelopeFromJson(json);
}
/// Push request for batched local changes.
@freezed
abstract class SyncPushRequest with _$SyncPushRequest {
@JsonSerializable(explicitToJson: true)
const factory SyncPushRequest({
required String deviceId,
String? cursor,
required List<ChangeEnvelope> changes,
required String idempotencyKey,
}) = _SyncPushRequest;
factory SyncPushRequest.fromJson(Map<String, dynamic> json) =>
_$SyncPushRequestFromJson(json);
}
/// Acknowledgement for an accepted mutation.
@freezed
abstract class MutationAck with _$MutationAck {
const factory MutationAck({
required String clientMutationId,
String? serverMutationId,
DateTime? acceptedAt,
}) = _MutationAck;
factory MutationAck.fromJson(Map<String, dynamic> json) =>
_$MutationAckFromJson(json);
}
/// Rejection detail for a mutation.
@freezed
abstract class MutationRejection with _$MutationRejection {
const factory MutationRejection({
required String clientMutationId,
required String code,
required String message,
}) = _MutationRejection;
factory MutationRejection.fromJson(Map<String, dynamic> json) =>
_$MutationRejectionFromJson(json);
}
/// Result body returned by sync push.
@freezed
abstract class SyncPushResult with _$SyncPushResult {
@JsonSerializable(explicitToJson: true)
const factory SyncPushResult({
required String cursor,
@Default(<MutationAck>[]) List<MutationAck> accepted,
@Default(<MutationRejection>[]) List<MutationRejection> rejected,
}) = _SyncPushResult;
factory SyncPushResult.fromJson(Map<String, dynamic> json) =>
_$SyncPushResultFromJson(json);
}
/// Result body returned by sync pull.
@freezed
abstract class SyncPullResult with _$SyncPullResult {
@JsonSerializable(explicitToJson: true)
const factory SyncPullResult({
required String cursor,
@Default(<ChangeEnvelope>[]) List<ChangeEnvelope> changes,
}) = _SyncPullResult;
factory SyncPullResult.fromJson(Map<String, dynamic> json) =>
_$SyncPullResultFromJson(json);
}
/// Signals feed item.
@freezed
abstract class SignalItem with _$SignalItem {
const factory SignalItem({
required String id,
required String type,
required String title,
String? description,
String? personId,
required DateTime createdAt,
Map<String, dynamic>? metadata,
}) = _SignalItem;
factory SignalItem.fromJson(Map<String, dynamic> json) =>
_$SignalItemFromJson(json);
}
/// Cursor-based signals response.
@freezed
abstract class SignalsFeed with _$SignalsFeed {
@JsonSerializable(explicitToJson: true)
const factory SignalsFeed({
String? cursor,
@Default(<SignalItem>[]) List<SignalItem> items,
}) = _SignalsFeed;
factory SignalsFeed.fromJson(Map<String, dynamic> json) =>
_$SignalsFeedFromJson(json);
}
/// Signal acknowledgement request body.
@freezed
abstract class SignalAckRequest with _$SignalAckRequest {
const factory SignalAckRequest({
required SignalAction action,
required DateTime at,
}) = _SignalAckRequest;
factory SignalAckRequest.fromJson(Map<String, dynamic> json) =>
_$SignalAckRequestFromJson(json);
}
/// Upload init request.
@freezed
abstract class UploadInitRequest with _$UploadInitRequest {
const factory UploadInitRequest({
required UploadPurpose purpose,
required String contentType,
required int sizeBytes,
}) = _UploadInitRequest;
factory UploadInitRequest.fromJson(Map<String, dynamic> json) =>
_$UploadInitRequestFromJson(json);
}
/// Upload init response.
@freezed
abstract class UploadInitResult with _$UploadInitResult {
const factory UploadInitResult({
required String uploadUrl,
required String fileId,
required DateTime expiresAt,
}) = _UploadInitResult;
factory UploadInitResult.fromJson(Map<String, dynamic> json) =>
_$UploadInitResultFromJson(json);
}
/// Download URL response.
@freezed
abstract class DownloadUrl with _$DownloadUrl {
const factory DownloadUrl({
required String downloadUrl,
required DateTime expiresAt,
}) = _DownloadUrl;
factory DownloadUrl.fromJson(Map<String, dynamic> json) =>
_$DownloadUrlFromJson(json);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,293 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'backend_models.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_SignInRequest _$SignInRequestFromJson(Map<String, dynamic> json) =>
_SignInRequest(
method: $enumDecode(_$SignInMethodEnumMap, json['method']),
email: json['email'] as String?,
password: json['password'] as String?,
idToken: json['idToken'] as String?,
);
Map<String, dynamic> _$SignInRequestToJson(_SignInRequest instance) =>
<String, dynamic>{
'method': _$SignInMethodEnumMap[instance.method]!,
'email': instance.email,
'password': instance.password,
'idToken': instance.idToken,
};
const _$SignInMethodEnumMap = {
SignInMethod.emailMagicLink: 'email_magic_link',
SignInMethod.password: 'password',
SignInMethod.oidc: 'oidc',
};
_UserProfile _$UserProfileFromJson(Map<String, dynamic> json) => _UserProfile(
id: json['id'] as String,
email: json['email'] as String?,
displayName: json['displayName'] as String?,
avatarFileId: json['avatarFileId'] as String?,
);
Map<String, dynamic> _$UserProfileToJson(_UserProfile instance) =>
<String, dynamic>{
'id': instance.id,
'email': instance.email,
'displayName': instance.displayName,
'avatarFileId': instance.avatarFileId,
};
_AuthSession _$AuthSessionFromJson(Map<String, dynamic> json) => _AuthSession(
accessToken: json['accessToken'] as String,
refreshToken: json['refreshToken'] as String,
expiresAt: DateTime.parse(json['expiresAt'] as String),
user: UserProfile.fromJson(json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$AuthSessionToJson(_AuthSession instance) =>
<String, dynamic>{
'accessToken': instance.accessToken,
'refreshToken': instance.refreshToken,
'expiresAt': instance.expiresAt.toIso8601String(),
'user': instance.user.toJson(),
};
_AuthRefreshRequest _$AuthRefreshRequestFromJson(Map<String, dynamic> json) =>
_AuthRefreshRequest(refreshToken: json['refreshToken'] as String);
Map<String, dynamic> _$AuthRefreshRequestToJson(_AuthRefreshRequest instance) =>
<String, dynamic>{'refreshToken': instance.refreshToken};
_AuthRefreshResponse _$AuthRefreshResponseFromJson(Map<String, dynamic> json) =>
_AuthRefreshResponse(
accessToken: json['accessToken'] as String,
refreshToken: json['refreshToken'] as String?,
expiresAt: DateTime.parse(json['expiresAt'] as String),
);
Map<String, dynamic> _$AuthRefreshResponseToJson(
_AuthRefreshResponse instance,
) => <String, dynamic>{
'accessToken': instance.accessToken,
'refreshToken': instance.refreshToken,
'expiresAt': instance.expiresAt.toIso8601String(),
};
_ChangeEnvelope _$ChangeEnvelopeFromJson(Map<String, dynamic> json) =>
_ChangeEnvelope(
schemaVersion: (json['schemaVersion'] as num).toInt(),
entityType: json['entityType'] as String,
entityId: json['entityId'] as String,
op: $enumDecode(_$ChangeOperationEnumMap, json['op']),
modifiedAt: DateTime.parse(json['modifiedAt'] as String),
clientMutationId: json['clientMutationId'] as String,
payload: json['payload'] as Map<String, dynamic>?,
);
Map<String, dynamic> _$ChangeEnvelopeToJson(_ChangeEnvelope instance) =>
<String, dynamic>{
'schemaVersion': instance.schemaVersion,
'entityType': instance.entityType,
'entityId': instance.entityId,
'op': _$ChangeOperationEnumMap[instance.op]!,
'modifiedAt': instance.modifiedAt.toIso8601String(),
'clientMutationId': instance.clientMutationId,
'payload': instance.payload,
};
const _$ChangeOperationEnumMap = {
ChangeOperation.upsert: 'upsert',
ChangeOperation.delete: 'delete',
};
_SyncPushRequest _$SyncPushRequestFromJson(Map<String, dynamic> json) =>
_SyncPushRequest(
deviceId: json['deviceId'] as String,
cursor: json['cursor'] as String?,
changes: (json['changes'] as List<dynamic>)
.map((e) => ChangeEnvelope.fromJson(e as Map<String, dynamic>))
.toList(),
idempotencyKey: json['idempotencyKey'] as String,
);
Map<String, dynamic> _$SyncPushRequestToJson(_SyncPushRequest instance) =>
<String, dynamic>{
'deviceId': instance.deviceId,
'cursor': instance.cursor,
'changes': instance.changes.map((e) => e.toJson()).toList(),
'idempotencyKey': instance.idempotencyKey,
};
_MutationAck _$MutationAckFromJson(Map<String, dynamic> json) => _MutationAck(
clientMutationId: json['clientMutationId'] as String,
serverMutationId: json['serverMutationId'] as String?,
acceptedAt: json['acceptedAt'] == null
? null
: DateTime.parse(json['acceptedAt'] as String),
);
Map<String, dynamic> _$MutationAckToJson(_MutationAck instance) =>
<String, dynamic>{
'clientMutationId': instance.clientMutationId,
'serverMutationId': instance.serverMutationId,
'acceptedAt': instance.acceptedAt?.toIso8601String(),
};
_MutationRejection _$MutationRejectionFromJson(Map<String, dynamic> json) =>
_MutationRejection(
clientMutationId: json['clientMutationId'] as String,
code: json['code'] as String,
message: json['message'] as String,
);
Map<String, dynamic> _$MutationRejectionToJson(_MutationRejection instance) =>
<String, dynamic>{
'clientMutationId': instance.clientMutationId,
'code': instance.code,
'message': instance.message,
};
_SyncPushResult _$SyncPushResultFromJson(Map<String, dynamic> json) =>
_SyncPushResult(
cursor: json['cursor'] as String,
accepted:
(json['accepted'] as List<dynamic>?)
?.map((e) => MutationAck.fromJson(e as Map<String, dynamic>))
.toList() ??
const <MutationAck>[],
rejected:
(json['rejected'] as List<dynamic>?)
?.map(
(e) => MutationRejection.fromJson(e as Map<String, dynamic>),
)
.toList() ??
const <MutationRejection>[],
);
Map<String, dynamic> _$SyncPushResultToJson(_SyncPushResult instance) =>
<String, dynamic>{
'cursor': instance.cursor,
'accepted': instance.accepted.map((e) => e.toJson()).toList(),
'rejected': instance.rejected.map((e) => e.toJson()).toList(),
};
_SyncPullResult _$SyncPullResultFromJson(Map<String, dynamic> json) =>
_SyncPullResult(
cursor: json['cursor'] as String,
changes:
(json['changes'] as List<dynamic>?)
?.map((e) => ChangeEnvelope.fromJson(e as Map<String, dynamic>))
.toList() ??
const <ChangeEnvelope>[],
);
Map<String, dynamic> _$SyncPullResultToJson(_SyncPullResult instance) =>
<String, dynamic>{
'cursor': instance.cursor,
'changes': instance.changes.map((e) => e.toJson()).toList(),
};
_SignalItem _$SignalItemFromJson(Map<String, dynamic> json) => _SignalItem(
id: json['id'] as String,
type: json['type'] as String,
title: json['title'] as String,
description: json['description'] as String?,
personId: json['personId'] as String?,
createdAt: DateTime.parse(json['createdAt'] as String),
metadata: json['metadata'] as Map<String, dynamic>?,
);
Map<String, dynamic> _$SignalItemToJson(_SignalItem instance) =>
<String, dynamic>{
'id': instance.id,
'type': instance.type,
'title': instance.title,
'description': instance.description,
'personId': instance.personId,
'createdAt': instance.createdAt.toIso8601String(),
'metadata': instance.metadata,
};
_SignalsFeed _$SignalsFeedFromJson(Map<String, dynamic> json) => _SignalsFeed(
cursor: json['cursor'] as String?,
items:
(json['items'] as List<dynamic>?)
?.map((e) => SignalItem.fromJson(e as Map<String, dynamic>))
.toList() ??
const <SignalItem>[],
);
Map<String, dynamic> _$SignalsFeedToJson(_SignalsFeed instance) =>
<String, dynamic>{
'cursor': instance.cursor,
'items': instance.items.map((e) => e.toJson()).toList(),
};
_SignalAckRequest _$SignalAckRequestFromJson(Map<String, dynamic> json) =>
_SignalAckRequest(
action: $enumDecode(_$SignalActionEnumMap, json['action']),
at: DateTime.parse(json['at'] as String),
);
Map<String, dynamic> _$SignalAckRequestToJson(_SignalAckRequest instance) =>
<String, dynamic>{
'action': _$SignalActionEnumMap[instance.action]!,
'at': instance.at.toIso8601String(),
};
const _$SignalActionEnumMap = {
SignalAction.viewed: 'viewed',
SignalAction.dismissed: 'dismissed',
SignalAction.saved: 'saved',
SignalAction.purchased: 'purchased',
};
_UploadInitRequest _$UploadInitRequestFromJson(Map<String, dynamic> json) =>
_UploadInitRequest(
purpose: $enumDecode(_$UploadPurposeEnumMap, json['purpose']),
contentType: json['contentType'] as String,
sizeBytes: (json['sizeBytes'] as num).toInt(),
);
Map<String, dynamic> _$UploadInitRequestToJson(_UploadInitRequest instance) =>
<String, dynamic>{
'purpose': _$UploadPurposeEnumMap[instance.purpose]!,
'contentType': instance.contentType,
'sizeBytes': instance.sizeBytes,
};
const _$UploadPurposeEnumMap = {
UploadPurpose.avatar: 'avatar',
UploadPurpose.attachment: 'attachment',
};
_UploadInitResult _$UploadInitResultFromJson(Map<String, dynamic> json) =>
_UploadInitResult(
uploadUrl: json['uploadUrl'] as String,
fileId: json['fileId'] as String,
expiresAt: DateTime.parse(json['expiresAt'] as String),
);
Map<String, dynamic> _$UploadInitResultToJson(_UploadInitResult instance) =>
<String, dynamic>{
'uploadUrl': instance.uploadUrl,
'fileId': instance.fileId,
'expiresAt': instance.expiresAt.toIso8601String(),
};
_DownloadUrl _$DownloadUrlFromJson(Map<String, dynamic> json) => _DownloadUrl(
downloadUrl: json['downloadUrl'] as String,
expiresAt: DateTime.parse(json['expiresAt'] as String),
);
Map<String, dynamic> _$DownloadUrlToJson(_DownloadUrl instance) =>
<String, dynamic>{
'downloadUrl': instance.downloadUrl,
'expiresAt': instance.expiresAt.toIso8601String(),
};