feat: Add Nanaxe server with Docker, GitHub Actions CI/CD, and auto-deployment

- Add complete server infrastructure with Gitea, API, PostgreSQL
- Implement Obsidian-compatible markdown mindmap converter
- Add Flutter service for server synchronization
- Configure GitHub Actions for automated build and deploy
- Add production docker-compose with health checks
- Include VPS setup and deployment scripts
- Add comprehensive deployment documentation
This commit is contained in:
2026-04-19 22:18:25 +05:00
parent 52aea9ac87
commit f1e0f6b5ef
56 changed files with 8892 additions and 397 deletions
+88
View File
@@ -0,0 +1,88 @@
import 'package:cloud_firestore/cloud_firestore.dart';
class MindMapModel {
final String id;
final String title;
final String description;
final String ownerEmail;
final bool isMain; // Основные карты (админские) vs Личные
final List<Map<String, dynamic>> nodes;
final Map<String, dynamic> settings;
final DateTime createdAt;
final DateTime updatedAt;
MindMapModel({
required this.id,
required this.title,
this.description = '',
required this.ownerEmail,
this.isMain = false,
this.nodes = const [],
this.settings = const {},
required this.createdAt,
required this.updatedAt,
});
factory MindMapModel.fromFirestore(DocumentSnapshot doc) {
final data = doc.data() as Map<String, dynamic>;
return MindMapModel.fromMap(doc.id, data);
}
factory MindMapModel.fromMap(String id, Map<String, dynamic> data) {
return MindMapModel(
id: id,
title: data['title'] ?? '',
description: data['description'] ?? '',
ownerEmail: data['ownerEmail'] ?? '',
isMain: data['isMain'] ?? false,
nodes: List<Map<String, dynamic>>.from(data['nodes'] ?? []),
settings: data['settings'] ?? {},
createdAt: (data['createdAt'] as Timestamp).toDate(),
updatedAt: (data['updatedAt'] as Timestamp).toDate(),
);
}
Map<String, dynamic> toFirestore() {
return {
'title': title,
'description': description,
'ownerEmail': ownerEmail,
'isMain': isMain,
'nodes': nodes,
'settings': settings,
'createdAt': Timestamp.fromDate(createdAt),
'updatedAt': Timestamp.fromDate(updatedAt),
};
}
MindMapModel copyWith({
String? id,
String? title,
String? description,
String? ownerEmail,
bool? isMain,
List<Map<String, dynamic>>? nodes,
Map<String, dynamic>? settings,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return MindMapModel(
id: id ?? this.id,
title: title ?? this.title,
description: description ?? this.description,
ownerEmail: ownerEmail ?? this.ownerEmail,
isMain: isMain ?? this.isMain,
nodes: nodes ?? this.nodes,
settings: settings ?? this.settings,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
int get nodeCount => nodes.length;
int get maxLevel {
if (nodes.isEmpty) return 0;
return nodes.fold(
0, (max, node) => max > (node['level'] ?? 0) ? max : (node['level'] ?? 0));
}
}