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:
@@ -1,13 +1,15 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:reactive_mind_map/reactive_mind_map.dart' as rm;
|
||||
import '../models/mind_map_model.dart';
|
||||
import '../models/task_model.dart';
|
||||
import '../core/firestore_service.dart';
|
||||
import '../core/local_storage_service.dart';
|
||||
import '../core/deepseek_service.dart';
|
||||
import '../core/mind_map_adapter.dart';
|
||||
|
||||
// Provider для mind-карты проекта
|
||||
final mindMapProvider = StateNotifierProvider.family<MindMapNotifier, AsyncValue<MindMapData>, String>((ref, projectId) {
|
||||
final mindMapProvider = StateNotifierProvider.family<MindMapNotifier,
|
||||
AsyncValue<MindMapData>, String>((ref, projectId) {
|
||||
return MindMapNotifier(projectId);
|
||||
});
|
||||
|
||||
@@ -31,9 +33,16 @@ class MindMapData {
|
||||
|
||||
// Получить родительский узел
|
||||
MindMapNode? getParent(String nodeId) {
|
||||
final node = nodes.firstWhere((n) => n.id == nodeId, orElse: () => throw Exception('Node not found'));
|
||||
final node = nodes.firstWhere((n) => n.id == nodeId,
|
||||
orElse: () => throw Exception('Node not found'));
|
||||
if (node.parentId == null) return null;
|
||||
return nodes.firstWhere((n) => n.id == node.parentId, orElse: () => throw Exception('Parent not found'));
|
||||
return nodes.firstWhere((n) => n.id == node.parentId,
|
||||
orElse: () => throw Exception('Parent not found'));
|
||||
}
|
||||
|
||||
// Конвертировать в reactive_mind_map формат
|
||||
rm.MindMapData toReactiveMindMapData() {
|
||||
return MindMapAdapter.nodesToMindMapData(nodes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,8 +58,9 @@ class MindMapNotifier extends StateNotifier<AsyncValue<MindMapData>> {
|
||||
try {
|
||||
// Загружаем из локального хранилища
|
||||
final localNodes = localStorageService.getMindMapNodes();
|
||||
final projectNodes = localNodes.where((n) => n.projectId == projectId).toList();
|
||||
|
||||
final projectNodes =
|
||||
localNodes.where((n) => n.projectId == projectId).toList();
|
||||
|
||||
state = AsyncValue.data(_createMindMapData(projectNodes));
|
||||
|
||||
// TODO: Загрузить из Firestore и синхронизировать
|
||||
@@ -90,7 +100,7 @@ class MindMapNotifier extends StateNotifier<AsyncValue<MindMapData>> {
|
||||
String icon = 'folder',
|
||||
}) async {
|
||||
try {
|
||||
final parentNode = parentId != null
|
||||
final parentNode = parentId != null
|
||||
? state.value?.nodes.firstWhere((n) => n.id == parentId)
|
||||
: null;
|
||||
|
||||
@@ -123,11 +133,11 @@ class MindMapNotifier extends StateNotifier<AsyncValue<MindMapData>> {
|
||||
final currentData = state.value;
|
||||
if (currentData != null) {
|
||||
final updatedNodes = [...currentData.nodes, node];
|
||||
|
||||
|
||||
if (parentId != null) {
|
||||
currentData.childrenMap.putIfAbsent(parentId, () => []).add(node.id);
|
||||
}
|
||||
|
||||
|
||||
state = AsyncValue.data(MindMapData(
|
||||
nodes: updatedNodes,
|
||||
childrenMap: currentData.childrenMap,
|
||||
@@ -143,7 +153,8 @@ class MindMapNotifier extends StateNotifier<AsyncValue<MindMapData>> {
|
||||
}
|
||||
|
||||
// Обновление узла
|
||||
Future<void> updateNode(String nodeId, {
|
||||
Future<void> updateNode(
|
||||
String nodeId, {
|
||||
String? title,
|
||||
String? color,
|
||||
String? icon,
|
||||
@@ -203,7 +214,9 @@ class MindMapNotifier extends StateNotifier<AsyncValue<MindMapData>> {
|
||||
_collectChildNodes(nodeId, currentData, nodesToDelete);
|
||||
|
||||
// Сохраняем для удаления
|
||||
final nodes = currentData.nodes.where((n) => !nodesToDelete.contains(n.id)).toList();
|
||||
final nodes = currentData.nodes
|
||||
.where((n) => !nodesToDelete.contains(n.id))
|
||||
.toList();
|
||||
|
||||
// Сохраняем локально (удаляем)
|
||||
for (final id in nodesToDelete) {
|
||||
@@ -234,7 +247,8 @@ class MindMapNotifier extends StateNotifier<AsyncValue<MindMapData>> {
|
||||
}
|
||||
|
||||
// Рекурсивный сбор дочерних узлов
|
||||
void _collectChildNodes(String parentId, MindMapData data, Set<String> result) {
|
||||
void _collectChildNodes(
|
||||
String parentId, MindMapData data, Set<String> result) {
|
||||
final childIds = data.childrenMap[parentId] ?? [];
|
||||
for (final childId in childIds) {
|
||||
result.add(childId);
|
||||
@@ -260,7 +274,7 @@ class MindMapNotifier extends StateNotifier<AsyncValue<MindMapData>> {
|
||||
final nodeData = nodesData[i];
|
||||
final level = nodeData['level'] as int;
|
||||
final parentId = nodeData['parentId'] as int?;
|
||||
|
||||
|
||||
String? actualParentId;
|
||||
if (parentId != null && parentId < nodes.length) {
|
||||
actualParentId = nodes[parentId].id;
|
||||
@@ -272,16 +286,16 @@ class MindMapNotifier extends StateNotifier<AsyncValue<MindMapData>> {
|
||||
x: (level * 200).toDouble(), // Автоматическое позиционирование
|
||||
y: (i * 50).toDouble(),
|
||||
);
|
||||
|
||||
|
||||
nodes.add(node);
|
||||
|
||||
|
||||
if (level == 0) {
|
||||
rootNode = node;
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
} catch (e, st) {
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
@@ -300,16 +314,18 @@ class MindMapNotifier extends StateNotifier<AsyncValue<MindMapData>> {
|
||||
return {
|
||||
'projectId': projectId,
|
||||
'rootNodeId': data.rootNodeId,
|
||||
'nodes': data.nodes.map((n) => {
|
||||
'id': n.id,
|
||||
'parentId': n.parentId,
|
||||
'level': n.level,
|
||||
'title': n.title,
|
||||
'x': n.x,
|
||||
'y': n.y,
|
||||
'color': n.color,
|
||||
'icon': n.icon,
|
||||
}).toList(),
|
||||
'nodes': data.nodes
|
||||
.map((n) => {
|
||||
'id': n.id,
|
||||
'parentId': n.parentId,
|
||||
'level': n.level,
|
||||
'title': n.title,
|
||||
'x': n.x,
|
||||
'y': n.y,
|
||||
'color': n.color,
|
||||
'icon': n.icon,
|
||||
})
|
||||
.toList(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user