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,4 +1,5 @@
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import '../models/task_model.dart';
|
||||
import '../models/alarm_model.dart';
|
||||
import '../models/mind_map_model.dart';
|
||||
@@ -29,20 +30,23 @@ class LocalStorageService {
|
||||
|
||||
// Tasks
|
||||
Future<void> saveTask(TaskModel task) async {
|
||||
await _tasksBox.put(task.id, task.toMap()); // Используем toMap() для Hive
|
||||
final data = _convertTimestamps(task.toFirestore());
|
||||
data['id'] = task.id; // Добавляем id для fromMap
|
||||
await _tasksBox.put(task.id, data);
|
||||
}
|
||||
|
||||
Future<void> saveTasks(List<TaskModel> tasks) async {
|
||||
for (final task in tasks) {
|
||||
await _tasksBox.put(task.id, task.toMap());
|
||||
final data = _convertTimestamps(task.toFirestore());
|
||||
data['id'] = task.id;
|
||||
await _tasksBox.put(task.id, data);
|
||||
}
|
||||
}
|
||||
|
||||
List<TaskModel> getTasks() {
|
||||
return _tasksBox.values.map((data) {
|
||||
return TaskModel.fromFirestore(
|
||||
_createMockDocument(data as Map<String, dynamic>),
|
||||
);
|
||||
final restoredData = _restoreTimestamps(data);
|
||||
return TaskModel.fromMap(restoredData);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@@ -52,14 +56,14 @@ class LocalStorageService {
|
||||
|
||||
// Alarms
|
||||
Future<void> saveAlarm(AlarmModel alarm) async {
|
||||
await _alarmsBox.put(alarm.id, alarm.toFirestore());
|
||||
await _alarmsBox.put(alarm.id, _convertTimestamps(alarm.toFirestore()));
|
||||
}
|
||||
|
||||
List<AlarmModel> getAlarms() {
|
||||
return _alarmsBox.values.map((data) {
|
||||
return AlarmModel.fromFirestore(
|
||||
_createMockDocument(data as Map<String, dynamic>),
|
||||
);
|
||||
return _alarmsBox.keys.map((id) {
|
||||
final data = _alarmsBox.get(id) as Map<dynamic, dynamic>;
|
||||
final restoredData = _restoreTimestamps(data);
|
||||
return AlarmModel.fromMap(id.toString(), restoredData);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@@ -70,28 +74,28 @@ class LocalStorageService {
|
||||
// Mind Maps
|
||||
Future<void> saveMindMapNodes(List<MindMapNode> nodes) async {
|
||||
for (final node in nodes) {
|
||||
await _mindMapsBox.put(node.id, node.toFirestore());
|
||||
await _mindMapsBox.put(node.id, _convertTimestamps(node.toFirestore()));
|
||||
}
|
||||
}
|
||||
|
||||
List<MindMapNode> getMindMapNodes() {
|
||||
return _mindMapsBox.values.map((data) {
|
||||
return MindMapNode.fromFirestore(
|
||||
_createMockDocument(data as Map<String, dynamic>),
|
||||
);
|
||||
return _mindMapsBox.keys.map((id) {
|
||||
final data = _mindMapsBox.get(id) as Map<dynamic, dynamic>;
|
||||
final restoredData = _restoreTimestamps(data);
|
||||
return MindMapNode.fromMap(id.toString(), restoredData);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// Events
|
||||
Future<void> saveEvent(CalendarEventModel event) async {
|
||||
await _eventsBox.put(event.id, event.toFirestore());
|
||||
await _eventsBox.put(event.id, _convertTimestamps(event.toFirestore()));
|
||||
}
|
||||
|
||||
List<CalendarEventModel> getEvents() {
|
||||
return _eventsBox.values.map((data) {
|
||||
return CalendarEventModel.fromFirestore(
|
||||
_createMockDocument(data as Map<String, dynamic>),
|
||||
);
|
||||
return _eventsBox.keys.map((id) {
|
||||
final data = _eventsBox.get(id) as Map<dynamic, dynamic>;
|
||||
final restoredData = _restoreTimestamps(data);
|
||||
return CalendarEventModel.fromMap(id.toString(), restoredData);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@@ -124,14 +128,49 @@ class LocalStorageService {
|
||||
|
||||
await _tasksBox.clear();
|
||||
for (final task in merged.values) {
|
||||
await _tasksBox.put(task.id, task.toFirestore());
|
||||
await _tasksBox.put(task.id, _convertTimestamps(task.toFirestore()));
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to create mock DocumentSnapshot
|
||||
dynamic _createMockDocument(Map<String, dynamic> data) {
|
||||
// Простая эмуляция для конвертации
|
||||
return data;
|
||||
// Конвертирует Firestore Timestamp в ISO строки для Hive
|
||||
Map<String, dynamic> _convertTimestamps(Map<String, dynamic> data) {
|
||||
final converted = <String, dynamic>{};
|
||||
for (final entry in data.entries) {
|
||||
if (entry.value is Timestamp) {
|
||||
// Конвертируем Timestamp → ISO строка
|
||||
converted[entry.key] = {
|
||||
'__type__': 'timestamp',
|
||||
'value': (entry.value as Timestamp).toDate().toIso8601String(),
|
||||
};
|
||||
} else if (entry.value is Map<String, dynamic>) {
|
||||
converted[entry.key] =
|
||||
_convertTimestamps(entry.value as Map<String, dynamic>);
|
||||
} else {
|
||||
converted[entry.key] = entry.value;
|
||||
}
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
// Восстанавливает ISO строки обратно в Timestamp из Hive
|
||||
Map<String, dynamic> _restoreTimestamps(Map<dynamic, dynamic> data) {
|
||||
final converted = <String, dynamic>{};
|
||||
for (final entry in data.entries) {
|
||||
final key = entry.key.toString();
|
||||
if (entry.value is Map &&
|
||||
(entry.value as Map).containsKey('__type__') &&
|
||||
(entry.value as Map)['__type__'] == 'timestamp') {
|
||||
// Конвертируем ISO строку → Timestamp
|
||||
final dateStr = (entry.value as Map)['value'] as String;
|
||||
converted[key] = Timestamp.fromDate(DateTime.parse(dateStr));
|
||||
} else if (entry.value is Map) {
|
||||
converted[key] =
|
||||
_restoreTimestamps(entry.value as Map<dynamic, dynamic>);
|
||||
} else {
|
||||
converted[key] = entry.value;
|
||||
}
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user