Implement loading meilisearch config from disk
Encrypted such as it is
This commit is contained in:
@@ -15,6 +15,10 @@ Journaler helps you build a consistent journaling habit by periodically remindin
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Note
|
||||||
|
|
||||||
|
**Versions up to 3 use a local sqlite database while versions 4 and above use meillisearch!**
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Automated Reminders**: Customizable popup intervals to remind you to journal
|
- **Automated Reminders**: Customizable popup intervals to remind you to journal
|
||||||
|
@@ -3,21 +3,24 @@ import 'dart:core';
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:journaler/notes.dart';
|
import 'package:journaler/notes.dart';
|
||||||
|
import 'package:journaler/meilisearch_config.dart';
|
||||||
|
|
||||||
const endpoint = 'https://meili.site.quack-lab.dev/';
|
|
||||||
const noteIndex = 'notes';
|
const noteIndex = 'notes';
|
||||||
const scratchIndex = 'scratch';
|
const scratchIndex = 'scratch';
|
||||||
const settingsIndex = 'settings';
|
const settingsIndex = 'settings';
|
||||||
const apiKey = String.fromEnvironment(
|
final alphanum = RegExp(r'[a-zA-Z0-9]', caseSensitive: false);
|
||||||
'MEILISEARCH_API_KEY',
|
|
||||||
defaultValue:
|
Future<Map<String, String>> _getHeaders() async {
|
||||||
'',
|
final apiKey = await getMeilisearchApiKey();
|
||||||
);
|
return {
|
||||||
const header = {
|
|
||||||
'Authorization': 'Bearer $apiKey',
|
'Authorization': 'Bearer $apiKey',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
};
|
};
|
||||||
final alphanum = RegExp(r'[a-zA-Z0-9]', caseSensitive: false);
|
}
|
||||||
|
|
||||||
|
Future<String> _getEndpoint() async {
|
||||||
|
return await getMeilisearchEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
class MeilisearchQuery {
|
class MeilisearchQuery {
|
||||||
String q;
|
String q;
|
||||||
@@ -93,65 +96,72 @@ class MeilisearchResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
|
|
||||||
if (!await indexExists(noteIndex)) {
|
if (!await indexExists(noteIndex)) {
|
||||||
await http.post(
|
await http.post(
|
||||||
Uri.parse('$endpoint/indexes'),
|
Uri.parse('$endpoint/indexes'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode({'uid': noteIndex, 'primaryKey': 'id'}),
|
body: jsonEncode({'uid': noteIndex, 'primaryKey': 'id'}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await http.put(
|
await http.put(
|
||||||
Uri.parse('$endpoint/indexes/$noteIndex/settings/sortable-attributes'),
|
Uri.parse('$endpoint/indexes/$noteIndex/settings/sortable-attributes'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(['date']),
|
body: jsonEncode(['date']),
|
||||||
);
|
);
|
||||||
await http.put(
|
await http.put(
|
||||||
Uri.parse('$endpoint/indexes/$noteIndex/settings/filterable-attributes'),
|
Uri.parse('$endpoint/indexes/$noteIndex/settings/filterable-attributes'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(['date', 'topLetter', 'topLetterFrequency']),
|
body: jsonEncode(['date', 'topLetter', 'topLetterFrequency']),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!await indexExists(scratchIndex)) {
|
if (!await indexExists(scratchIndex)) {
|
||||||
await http.post(
|
await http.post(
|
||||||
Uri.parse('$endpoint/indexes'),
|
Uri.parse('$endpoint/indexes'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode({'uid': scratchIndex, 'primaryKey': 'id'}),
|
body: jsonEncode({'uid': scratchIndex, 'primaryKey': 'id'}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await http.put(
|
await http.put(
|
||||||
Uri.parse('$endpoint/indexes/$scratchIndex/settings/sortable-attributes'),
|
Uri.parse('$endpoint/indexes/$scratchIndex/settings/sortable-attributes'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(['date']),
|
body: jsonEncode(['date']),
|
||||||
);
|
);
|
||||||
await http.put(
|
await http.put(
|
||||||
Uri.parse('$endpoint/indexes/$scratchIndex/settings/filterable-attributes'),
|
Uri.parse('$endpoint/indexes/$scratchIndex/settings/filterable-attributes'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(['date']),
|
body: jsonEncode(['date']),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!await indexExists(settingsIndex)) {
|
if (!await indexExists(settingsIndex)) {
|
||||||
await http.post(
|
await http.post(
|
||||||
Uri.parse('$endpoint/indexes'),
|
Uri.parse('$endpoint/indexes'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode({'uid': settingsIndex, 'primaryKey': 'key'}),
|
body: jsonEncode({'uid': settingsIndex, 'primaryKey': 'key'}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> indexExists(String index) async {
|
Future<bool> indexExists(String index) async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
final response = await http.get(
|
final response = await http.get(
|
||||||
Uri.parse('$endpoint/indexes/$index'),
|
Uri.parse('$endpoint/indexes/$index'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
);
|
);
|
||||||
return response.statusCode == 200;
|
return response.statusCode == 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings Management
|
// Settings Management
|
||||||
Future<String?> getSetting(String key) async {
|
Future<String?> getSetting(String key) async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
final searchCondition = MeilisearchQuery(q: '', filter: 'key = $key');
|
final searchCondition = MeilisearchQuery(q: '', filter: 'key = $key');
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse('$endpoint/indexes/$settingsIndex/search'),
|
Uri.parse('$endpoint/indexes/$settingsIndex/search'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(searchCondition.toJson()),
|
body: jsonEncode(searchCondition.toJson()),
|
||||||
);
|
);
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
@@ -165,10 +175,12 @@ Future<String?> getSetting(String key) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setSetting(String key, String value) async {
|
Future<void> setSetting(String key, String value) async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
final document = {'key': key, 'value': value};
|
final document = {'key': key, 'value': value};
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse('$endpoint/indexes/$settingsIndex/documents'),
|
Uri.parse('$endpoint/indexes/$settingsIndex/documents'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(document),
|
body: jsonEncode(document),
|
||||||
);
|
);
|
||||||
if (response.statusCode != 202) {
|
if (response.statusCode != 202) {
|
||||||
@@ -179,6 +191,8 @@ Future<void> setSetting(String key, String value) async {
|
|||||||
// Maybe we could factor a lot of this out into a separate function
|
// Maybe we could factor a lot of this out into a separate function
|
||||||
// But we don't care for now...
|
// But we don't care for now...
|
||||||
Future<List<Note>> searchNotes(String query) async {
|
Future<List<Note>> searchNotes(String query) async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
final searchCondition = MeilisearchQuery(
|
final searchCondition = MeilisearchQuery(
|
||||||
q: query,
|
q: query,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
@@ -189,7 +203,7 @@ Future<List<Note>> searchNotes(String query) async {
|
|||||||
);
|
);
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse('$endpoint/indexes/$noteIndex/search'),
|
Uri.parse('$endpoint/indexes/$noteIndex/search'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(searchCondition.toJson()),
|
body: jsonEncode(searchCondition.toJson()),
|
||||||
);
|
);
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
@@ -209,6 +223,8 @@ Future<List<Note>> searchNotes(String query) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Note?> getPreviousTo(int epochTime) async {
|
Future<Note?> getPreviousTo(int epochTime) async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
final searchCondition = MeilisearchQuery(
|
final searchCondition = MeilisearchQuery(
|
||||||
q: '',
|
q: '',
|
||||||
filter: 'date < $epochTime',
|
filter: 'date < $epochTime',
|
||||||
@@ -217,7 +233,7 @@ Future<Note?> getPreviousTo(int epochTime) async {
|
|||||||
);
|
);
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse('$endpoint/indexes/$noteIndex/search'),
|
Uri.parse('$endpoint/indexes/$noteIndex/search'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(searchCondition.toJson()),
|
body: jsonEncode(searchCondition.toJson()),
|
||||||
);
|
);
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
@@ -237,6 +253,8 @@ Future<Note?> getPreviousTo(int epochTime) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Note?> getNextTo(int epochTime) async {
|
Future<Note?> getNextTo(int epochTime) async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
final searchCondition = MeilisearchQuery(
|
final searchCondition = MeilisearchQuery(
|
||||||
q: '',
|
q: '',
|
||||||
filter: 'date > $epochTime',
|
filter: 'date > $epochTime',
|
||||||
@@ -245,7 +263,7 @@ Future<Note?> getNextTo(int epochTime) async {
|
|||||||
);
|
);
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse('$endpoint/indexes/$noteIndex/search'),
|
Uri.parse('$endpoint/indexes/$noteIndex/search'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(searchCondition.toJson()),
|
body: jsonEncode(searchCondition.toJson()),
|
||||||
);
|
);
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
@@ -265,6 +283,8 @@ Future<Note?> getNextTo(int epochTime) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Note?> getLatest() async {
|
Future<Note?> getLatest() async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
final searchCondition = MeilisearchQuery(
|
final searchCondition = MeilisearchQuery(
|
||||||
q: '',
|
q: '',
|
||||||
sort: ['date:desc'],
|
sort: ['date:desc'],
|
||||||
@@ -272,7 +292,7 @@ Future<Note?> getLatest() async {
|
|||||||
);
|
);
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse('$endpoint/indexes/$noteIndex/search'),
|
Uri.parse('$endpoint/indexes/$noteIndex/search'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(searchCondition.toJson()),
|
body: jsonEncode(searchCondition.toJson()),
|
||||||
);
|
);
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
@@ -303,6 +323,8 @@ String generateRandomString(int length) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Note> createNote(String content) async {
|
Future<Note> createNote(String content) async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
final lines = content.split('\n');
|
final lines = content.split('\n');
|
||||||
final trimmedLines = <String>[];
|
final trimmedLines = <String>[];
|
||||||
for (final line in lines) {
|
for (final line in lines) {
|
||||||
@@ -334,7 +356,7 @@ Future<Note> createNote(String content) async {
|
|||||||
};
|
};
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse('$endpoint/indexes/$noteIndex/documents'),
|
Uri.parse('$endpoint/indexes/$noteIndex/documents'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(document),
|
body: jsonEncode(document),
|
||||||
);
|
);
|
||||||
if (response.statusCode != 202) {
|
if (response.statusCode != 202) {
|
||||||
@@ -348,13 +370,15 @@ Future<Note> createNote(String content) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Note>> getProblematic({double threshold = 0.7}) async {
|
Future<List<Note>> getProblematic({double threshold = 0.7}) async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
final searchCondition = MeilisearchQuery(
|
final searchCondition = MeilisearchQuery(
|
||||||
q: '',
|
q: '',
|
||||||
filter: 'topLetterFrequency > $threshold',
|
filter: 'topLetterFrequency > $threshold',
|
||||||
);
|
);
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse('$endpoint/indexes/$noteIndex/search'),
|
Uri.parse('$endpoint/indexes/$noteIndex/search'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(searchCondition.toJson()),
|
body: jsonEncode(searchCondition.toJson()),
|
||||||
);
|
);
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
@@ -380,6 +404,8 @@ Future<List<Note>> getProblematic({double threshold = 0.7}) async {
|
|||||||
// TODO: only update if changed
|
// TODO: only update if changed
|
||||||
// How? idk
|
// How? idk
|
||||||
Future<void> updateNote(Note note) async {
|
Future<void> updateNote(Note note) async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
final lines = note.content.split('\n');
|
final lines = note.content.split('\n');
|
||||||
final trimmedLines = <String>[];
|
final trimmedLines = <String>[];
|
||||||
for (final line in lines) {
|
for (final line in lines) {
|
||||||
@@ -413,7 +439,7 @@ Future<void> updateNote(Note note) async {
|
|||||||
|
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse('$endpoint/indexes/$noteIndex/documents'),
|
Uri.parse('$endpoint/indexes/$noteIndex/documents'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(document),
|
body: jsonEncode(document),
|
||||||
);
|
);
|
||||||
if (response.statusCode != 202) {
|
if (response.statusCode != 202) {
|
||||||
@@ -424,9 +450,11 @@ Future<void> updateNote(Note note) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteNote(String id) async {
|
Future<void> deleteNote(String id) async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
final response = await http.delete(
|
final response = await http.delete(
|
||||||
Uri.parse('$endpoint/indexes/$noteIndex/documents/$id'),
|
Uri.parse('$endpoint/indexes/$noteIndex/documents/$id'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
);
|
);
|
||||||
if (response.statusCode != 202) {
|
if (response.statusCode != 202) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
@@ -436,6 +464,8 @@ Future<void> deleteNote(String id) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Scratch?> getLatestScratch() async {
|
Future<Scratch?> getLatestScratch() async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
final searchCondition = MeilisearchQuery(
|
final searchCondition = MeilisearchQuery(
|
||||||
q: '',
|
q: '',
|
||||||
sort: ['date:desc'],
|
sort: ['date:desc'],
|
||||||
@@ -443,7 +473,7 @@ Future<Scratch?> getLatestScratch() async {
|
|||||||
);
|
);
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse('$endpoint/indexes/$scratchIndex/search'),
|
Uri.parse('$endpoint/indexes/$scratchIndex/search'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(searchCondition.toJson()),
|
body: jsonEncode(searchCondition.toJson()),
|
||||||
);
|
);
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
@@ -463,6 +493,8 @@ Future<Scratch?> getLatestScratch() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Scratch> createScratch(String content) async {
|
Future<Scratch> createScratch(String content) async {
|
||||||
|
final endpoint = await _getEndpoint();
|
||||||
|
final headers = await _getHeaders();
|
||||||
final document = {
|
final document = {
|
||||||
'id': generateRandomString(32),
|
'id': generateRandomString(32),
|
||||||
'date': DateTime.now().toUtc().millisecondsSinceEpoch,
|
'date': DateTime.now().toUtc().millisecondsSinceEpoch,
|
||||||
@@ -470,7 +502,7 @@ Future<Scratch> createScratch(String content) async {
|
|||||||
};
|
};
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
Uri.parse('$endpoint/indexes/$scratchIndex/documents'),
|
Uri.parse('$endpoint/indexes/$scratchIndex/documents'),
|
||||||
headers: header,
|
headers: headers,
|
||||||
body: jsonEncode(document),
|
body: jsonEncode(document),
|
||||||
);
|
);
|
||||||
if (response.statusCode != 202) {
|
if (response.statusCode != 202) {
|
||||||
|
133
lib/meilisearch_config.dart
Normal file
133
lib/meilisearch_config.dart
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
const defaultEndpoint = 'http://localhost:7700';
|
||||||
|
const defaultApiKey = 'masterKey';
|
||||||
|
|
||||||
|
// Cache for configuration
|
||||||
|
String? _cachedEndpoint;
|
||||||
|
String? _cachedApiKey;
|
||||||
|
bool _isInitialized = false;
|
||||||
|
|
||||||
|
// Get the config file path in the user's home directory
|
||||||
|
Future<String> _getConfigPath() async {
|
||||||
|
final home =
|
||||||
|
Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];
|
||||||
|
if (home == null) {
|
||||||
|
throw Exception('Could not find home directory');
|
||||||
|
}
|
||||||
|
final configDir = Directory(path.join(home, '.journaler'));
|
||||||
|
if (!await configDir.exists()) {
|
||||||
|
await configDir.create(recursive: true);
|
||||||
|
}
|
||||||
|
return path.join(configDir.path, 'meilisearch_config.enc');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple encryption key derived from machine-specific data
|
||||||
|
String _getEncryptionKey() {
|
||||||
|
final machineId =
|
||||||
|
Platform.operatingSystem +
|
||||||
|
Platform.operatingSystemVersion +
|
||||||
|
Platform.localHostname;
|
||||||
|
return sha256.convert(utf8.encode(machineId)).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt data
|
||||||
|
String _encrypt(String data) {
|
||||||
|
final key = _getEncryptionKey();
|
||||||
|
final bytes = utf8.encode(data);
|
||||||
|
final encrypted = <int>[];
|
||||||
|
for (var i = 0; i < bytes.length; i++) {
|
||||||
|
encrypted.add(bytes[i] ^ key.codeUnitAt(i % key.length));
|
||||||
|
}
|
||||||
|
return base64.encode(encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt data
|
||||||
|
String _decrypt(String encrypted) {
|
||||||
|
final key = _getEncryptionKey();
|
||||||
|
final bytes = base64.decode(encrypted);
|
||||||
|
final decrypted = <int>[];
|
||||||
|
for (var i = 0; i < bytes.length; i++) {
|
||||||
|
decrypted.add(bytes[i] ^ key.codeUnitAt(i % key.length));
|
||||||
|
}
|
||||||
|
return utf8.decode(decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize cache from file
|
||||||
|
Future<void> _initializeCache() async {
|
||||||
|
if (_isInitialized) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final configPath = await _getConfigPath();
|
||||||
|
final file = File(configPath);
|
||||||
|
if (!await file.exists()) {
|
||||||
|
_cachedEndpoint = defaultEndpoint;
|
||||||
|
_cachedApiKey = defaultApiKey;
|
||||||
|
_isInitialized = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final encrypted = await file.readAsString();
|
||||||
|
final decrypted = _decrypt(encrypted);
|
||||||
|
final config = jsonDecode(decrypted);
|
||||||
|
|
||||||
|
_cachedEndpoint = config['endpoint'] ?? defaultEndpoint;
|
||||||
|
_cachedApiKey = config['apiKey'] ?? defaultApiKey;
|
||||||
|
} catch (e) {
|
||||||
|
_cachedEndpoint = defaultEndpoint;
|
||||||
|
_cachedApiKey = defaultApiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getMeilisearchEndpoint() async {
|
||||||
|
await _initializeCache();
|
||||||
|
return _cachedEndpoint!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getMeilisearchApiKey() async {
|
||||||
|
await _initializeCache();
|
||||||
|
return _cachedApiKey!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setMeilisearchEndpoint(String endpoint) async {
|
||||||
|
final configPath = await _getConfigPath();
|
||||||
|
final file = File(configPath);
|
||||||
|
|
||||||
|
Map<String, dynamic> config = {};
|
||||||
|
if (await file.exists()) {
|
||||||
|
final encrypted = await file.readAsString();
|
||||||
|
final decrypted = _decrypt(encrypted);
|
||||||
|
config = jsonDecode(decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
config['endpoint'] = endpoint;
|
||||||
|
final encrypted = _encrypt(jsonEncode(config));
|
||||||
|
await file.writeAsString(encrypted);
|
||||||
|
|
||||||
|
// Update cache
|
||||||
|
_cachedEndpoint = endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setMeilisearchApiKey(String apiKey) async {
|
||||||
|
final configPath = await _getConfigPath();
|
||||||
|
final file = File(configPath);
|
||||||
|
|
||||||
|
Map<String, dynamic> config = {};
|
||||||
|
if (await file.exists()) {
|
||||||
|
final encrypted = await file.readAsString();
|
||||||
|
final decrypted = _decrypt(encrypted);
|
||||||
|
config = jsonDecode(decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
config['apiKey'] = apiKey;
|
||||||
|
final encrypted = _encrypt(jsonEncode(config));
|
||||||
|
await file.writeAsString(encrypted);
|
||||||
|
|
||||||
|
// Update cache
|
||||||
|
_cachedApiKey = apiKey;
|
||||||
|
}
|
@@ -130,7 +130,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.19.1"
|
version: "1.19.1"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||||
|
@@ -42,6 +42,7 @@ dependencies:
|
|||||||
ps_list: ^0.0.5
|
ps_list: ^0.0.5
|
||||||
intl: ^0.20.2
|
intl: ^0.20.2
|
||||||
http: ^1.4.0
|
http: ^1.4.0
|
||||||
|
crypto: ^3.0.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user