98 lines
2.7 KiB
Dart
98 lines
2.7 KiB
Dart
import 'package:sqflite/sqflite.dart';
|
|
import 'package:path/path.dart';
|
|
import 'package:login_vault_app/constants.dart';
|
|
import 'package:login_vault_app/encryptor.dart';
|
|
|
|
/// A single saved login/private entry.
|
|
class VaultEntry {
|
|
final String id; // unique string
|
|
final String content; // decrypted plaintext
|
|
|
|
VaultEntry({required this.id, required this.content});
|
|
|
|
/// Build from a DB row (content is stored encrypted).
|
|
factory VaultEntry.fromDbRow(Map<String, dynamic> row) => VaultEntry(
|
|
id: row['id'] as String,
|
|
content: Encryptor.decrypt(row['content'] as String),
|
|
);
|
|
|
|
/// Build a DB row map (content encrypted).
|
|
Map<String, dynamic> toDbRow() => {
|
|
'id': id,
|
|
'content': Encryptor.encrypt(content),
|
|
};
|
|
}
|
|
|
|
class DatabaseHelper {
|
|
static final DatabaseHelper _instance = DatabaseHelper._internal();
|
|
factory DatabaseHelper() => _instance;
|
|
DatabaseHelper._internal();
|
|
|
|
Database? _db;
|
|
|
|
Future<Database> get db async {
|
|
_db ??= await _initDb();
|
|
return _db!;
|
|
}
|
|
|
|
Future<Database> _initDb() async {
|
|
final path = join(await getDatabasesPath(), AppConstants.dbName);
|
|
return openDatabase(
|
|
path,
|
|
version: 1,
|
|
onCreate: (db, version) async {
|
|
await db.execute('''
|
|
CREATE TABLE vault (
|
|
id TEXT PRIMARY KEY,
|
|
content TEXT NOT NULL
|
|
)
|
|
''');
|
|
},
|
|
);
|
|
}
|
|
|
|
/// Insert a new entry (throws if [id] already exists -> uniqueness).
|
|
Future<void> insert(VaultEntry entry) async {
|
|
final d = await db;
|
|
await d.insert('vault', entry.toDbRow(),
|
|
conflictAlgorithm: ConflictAlgorithm.fail);
|
|
}
|
|
|
|
/// Update content for an existing [id].
|
|
Future<void> update(String id, String newContent) async {
|
|
final d = await db;
|
|
await d.update(
|
|
'vault',
|
|
{'content': Encryptor.encrypt(newContent)},
|
|
where: 'id = ?',
|
|
whereArgs: [id],
|
|
);
|
|
}
|
|
|
|
/// Delete by [id].
|
|
Future<void> delete(String id) async {
|
|
final d = await db;
|
|
await d.delete('vault', where: 'id = ?', whereArgs: [id]);
|
|
}
|
|
|
|
/// Get all entries (used for full list / search).
|
|
Future<List<VaultEntry>> getAll() async {
|
|
final d = await db;
|
|
final rows = await d.query('vault', orderBy: 'id COLLATE NOCASE ASC');
|
|
return rows.map(VaultEntry.fromDbRow).toList();
|
|
}
|
|
|
|
/// Search ids containing [query] (case-insensitive). Returns matching entries.
|
|
Future<List<VaultEntry>> search(String query) async {
|
|
if (query.trim().isEmpty) return getAll();
|
|
final d = await db;
|
|
final rows = await d.query(
|
|
'vault',
|
|
where: 'id LIKE ?',
|
|
whereArgs: ['%${query.trim()}%'],
|
|
orderBy: 'id COLLATE NOCASE ASC',
|
|
);
|
|
return rows.map(VaultEntry.fromDbRow).toList();
|
|
}
|
|
}
|