Compare commits
12 Commits
5104af84dc
...
bdb8f0aac9
Author | SHA1 | Date | |
---|---|---|---|
bdb8f0aac9 | |||
29fb70149c | |||
4b45e71193 | |||
0101e92d97 | |||
ba5bde24e1 | |||
40d71223ae | |||
e73fb02101 | |||
7f300b33c8 | |||
c4c509790d | |||
1ac64c9d82 | |||
c037fd5657 | |||
9b9929e69d |
@ -12,6 +12,7 @@
|
||||
### Changes from v2.0.4-rc
|
||||
|
||||
* Continue documentation updates
|
||||
* Removed passwords and secret key values from API calls
|
||||
* Renamed setting `ApiAuth` to `ApiPassword`
|
||||
* Require `--name,-na` option for encryption provider
|
||||
|
||||
|
@ -314,6 +314,11 @@ provider_type_from_string(std::string_view type,
|
||||
|
||||
[[nodiscard]] auto provider_type_to_string(provider_type type) -> std::string;
|
||||
|
||||
void clean_json_config(provider_type prov, nlohmann::json &data);
|
||||
|
||||
[[nodiscard]] auto clean_json_value(std::string_view name,
|
||||
std::string_view data) -> std::string;
|
||||
|
||||
#if defined(_WIN32)
|
||||
struct open_file_data final {
|
||||
PVOID directory_buffer{nullptr};
|
||||
|
@ -39,6 +39,7 @@ server::server(app_config &config) : config_(config) {}
|
||||
void server::handle_get_config(const httplib::Request & /*req*/,
|
||||
httplib::Response &res) {
|
||||
auto data = config_.get_json();
|
||||
clean_json_config(config_.get_provider_type(), data);
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
@ -46,7 +47,10 @@ void server::handle_get_config(const httplib::Request & /*req*/,
|
||||
void server::handle_get_config_value_by_name(const httplib::Request &req,
|
||||
httplib::Response &res) {
|
||||
auto name = req.get_param_value("name");
|
||||
auto data = json({{"value", config_.get_value_by_name(name)}});
|
||||
auto data = json({{
|
||||
"value",
|
||||
clean_json_value(name, config_.get_value_by_name(name)),
|
||||
}});
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
@ -56,7 +60,10 @@ void server::handle_set_config_value_by_name(const httplib::Request &req,
|
||||
auto name = req.get_param_value("name");
|
||||
auto value = req.get_param_value("value");
|
||||
|
||||
json data = {{"value", config_.set_value_by_name(name, value)}};
|
||||
json data = {{
|
||||
"value",
|
||||
clean_json_value(name, config_.set_value_by_name(name, value)),
|
||||
}};
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
@ -26,6 +26,51 @@
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace repertory {
|
||||
void clean_json_config(provider_type prov, nlohmann::json &data) {
|
||||
data[JSON_API_PASSWORD] = "";
|
||||
|
||||
switch (prov) {
|
||||
case provider_type::encrypt:
|
||||
data[JSON_ENCRYPT_CONFIG][JSON_ENCRYPTION_TOKEN] = "";
|
||||
data[JSON_REMOTE_MOUNT][JSON_ENCRYPTION_TOKEN] = "";
|
||||
break;
|
||||
|
||||
case provider_type::remote:
|
||||
data[JSON_REMOTE_CONFIG][JSON_ENCRYPTION_TOKEN] = "";
|
||||
break;
|
||||
|
||||
case provider_type::s3:
|
||||
data[JSON_REMOTE_MOUNT][JSON_ENCRYPTION_TOKEN] = "";
|
||||
data[JSON_S3_CONFIG][JSON_ENCRYPTION_TOKEN] = "";
|
||||
data[JSON_S3_CONFIG][JSON_SECRET_KEY] = "";
|
||||
break;
|
||||
|
||||
case provider_type::sia:
|
||||
data[JSON_REMOTE_MOUNT][JSON_ENCRYPTION_TOKEN] = "";
|
||||
data[JSON_HOST_CONFIG][JSON_API_PASSWORD] = "";
|
||||
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto clean_json_value(std::string_view name, std::string_view data)
|
||||
-> std::string {
|
||||
if (name ==
|
||||
fmt::format("{}.{}", JSON_ENCRYPT_CONFIG, JSON_ENCRYPTION_TOKEN) ||
|
||||
name == fmt::format("{}.{}", JSON_HOST_CONFIG, JSON_API_PASSWORD) ||
|
||||
name == fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_ENCRYPTION_TOKEN) ||
|
||||
name == fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN) ||
|
||||
name == fmt::format("{}.{}", JSON_S3_CONFIG, JSON_ENCRYPTION_TOKEN) ||
|
||||
name == fmt::format("{}.{}", JSON_S3_CONFIG, JSON_SECRET_KEY) ||
|
||||
name == JSON_API_PASSWORD) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return std::string{data};
|
||||
}
|
||||
|
||||
auto database_type_from_string(std::string type, database_type default_type)
|
||||
-> database_type {
|
||||
type = utils::string::to_lower(utils::string::trim(type));
|
||||
|
@ -29,10 +29,33 @@
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/error_utils.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/hash.hpp"
|
||||
#include "utils/path.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] auto decrypt(std::string_view data, std::string_view password)
|
||||
-> std::string {
|
||||
auto decoded = macaron::Base64::Decode(data);
|
||||
repertory::data_buffer buffer(decoded.size());
|
||||
|
||||
auto key = repertory::utils::encryption::create_hash_blake2b_256(password);
|
||||
|
||||
std::uint64_t size{};
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
reinterpret_cast<unsigned char *>(buffer.data()), &size, nullptr,
|
||||
reinterpret_cast<const unsigned char *>(
|
||||
&decoded.at(crypto_aead_xchacha20poly1305_IETF_NPUBBYTES)),
|
||||
decoded.size() - crypto_aead_xchacha20poly1305_IETF_NPUBBYTES,
|
||||
reinterpret_cast<const unsigned char *>(REPERTORY.data()), 9U,
|
||||
reinterpret_cast<const unsigned char *>(decoded.data()),
|
||||
reinterpret_cast<const unsigned char *>(key.data()));
|
||||
|
||||
return std::string(
|
||||
buffer.begin(),
|
||||
std::next(buffer.begin(), static_cast<std::int64_t>(size)));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto is_restricted(std::string_view data) -> bool {
|
||||
constexpr std::string_view invalid_chars = "&;|><$()`{}!*?";
|
||||
return data.find_first_of(invalid_chars) != std::string_view::npos;
|
||||
@ -218,6 +241,8 @@ void handlers::handle_get_mount(auto &&req, auto &&res) const {
|
||||
lines.erase(lines.begin());
|
||||
|
||||
auto result = nlohmann::json::parse(utils::string::join(lines, '\n'));
|
||||
clean_json_config(prov, result);
|
||||
|
||||
res.set_content(result.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
@ -320,6 +345,7 @@ void handlers::handle_get_mount_status(auto &&req, auto &&res) const {
|
||||
|
||||
void handlers::handle_get_settings(auto &&res) const {
|
||||
auto settings = config_->to_json();
|
||||
settings[JSON_API_PASSWORD] = "";
|
||||
settings.erase(JSON_MOUNT_LOCATIONS);
|
||||
res.set_content(settings.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
@ -383,6 +409,7 @@ void handlers::handle_post_mount(auto &&req, auto &&res) const {
|
||||
|
||||
void handlers::handle_put_set_value_by_name(auto &&req, auto &&res) const {
|
||||
auto name = req.get_param_value("name");
|
||||
|
||||
auto prov = provider_type_from_string(req.get_param_value("type"));
|
||||
|
||||
if (not data_directory_exists(prov, name)) {
|
||||
@ -391,8 +418,19 @@ void handlers::handle_put_set_value_by_name(auto &&req, auto &&res) const {
|
||||
}
|
||||
|
||||
auto key = req.get_param_value("key");
|
||||
auto last_key{key};
|
||||
auto value = req.get_param_value("value");
|
||||
|
||||
auto parts = utils::string::split(key, '.', false);
|
||||
if (parts.size() > 1U) {
|
||||
last_key = parts.at(parts.size() - 1U);
|
||||
}
|
||||
|
||||
if (last_key == JSON_API_PASSWORD || last_key == JSON_ENCRYPTION_TOKEN ||
|
||||
last_key == JSON_SECRET_KEY) {
|
||||
value = decrypt(value, config_->get_api_password());
|
||||
}
|
||||
|
||||
set_key_value(prov, name, key, value);
|
||||
|
||||
res.status = http_error_codes::ok;
|
||||
@ -402,7 +440,9 @@ void handlers::handle_put_settings(auto &&req, auto &&res) const {
|
||||
nlohmann::json data = nlohmann::json::parse(req.get_param_value("data"));
|
||||
|
||||
if (data.contains(JSON_API_PASSWORD)) {
|
||||
config_->set_api_password(data.at(JSON_API_PASSWORD).get<std::string>());
|
||||
auto password = decrypt(data.at(JSON_API_PASSWORD).get<std::string>(),
|
||||
config_->get_api_password());
|
||||
config_->set_api_password(password);
|
||||
}
|
||||
|
||||
if (data.contains(JSON_API_PORT)) {
|
||||
|
198
repertory/repertory_test/src/clean_json_test.cpp
Normal file
198
repertory/repertory_test/src/clean_json_test.cpp
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TEST(clean_json_test, can_clean_values) {
|
||||
auto result = clean_json_value(JSON_API_PASSWORD, "moose");
|
||||
EXPECT_TRUE(result.empty());
|
||||
|
||||
result = clean_json_value(
|
||||
fmt::format("{}.{}", JSON_ENCRYPT_CONFIG, JSON_ENCRYPTION_TOKEN),
|
||||
"moose");
|
||||
EXPECT_TRUE(result.empty());
|
||||
|
||||
result = clean_json_value(
|
||||
fmt::format("{}.{}", JSON_HOST_CONFIG, JSON_API_PASSWORD), "moose");
|
||||
EXPECT_TRUE(result.empty());
|
||||
|
||||
result = clean_json_value(
|
||||
fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_ENCRYPTION_TOKEN), "moose");
|
||||
EXPECT_TRUE(result.empty());
|
||||
|
||||
result = clean_json_value(
|
||||
fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN), "moose");
|
||||
EXPECT_TRUE(result.empty());
|
||||
|
||||
result = clean_json_value(
|
||||
fmt::format("{}.{}", JSON_S3_CONFIG, JSON_ENCRYPTION_TOKEN), "moose");
|
||||
EXPECT_TRUE(result.empty());
|
||||
|
||||
result = clean_json_value(
|
||||
fmt::format("{}.{}", JSON_S3_CONFIG, JSON_SECRET_KEY), "moose");
|
||||
EXPECT_TRUE(result.empty());
|
||||
}
|
||||
|
||||
TEST(clean_json_test, can_clean_encrypt_config) {
|
||||
auto dir =
|
||||
utils::path::combine(test::get_test_output_dir(), {
|
||||
"clean_json_test",
|
||||
"encrypt",
|
||||
});
|
||||
app_config cfg(provider_type::encrypt, dir);
|
||||
cfg.set_value_by_name(JSON_API_PASSWORD, "moose");
|
||||
cfg.set_value_by_name(
|
||||
fmt::format("{}.{}", JSON_ENCRYPT_CONFIG, JSON_ENCRYPTION_TOKEN),
|
||||
"moose");
|
||||
cfg.set_value_by_name(
|
||||
fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN), "moose");
|
||||
|
||||
auto data = cfg.get_json();
|
||||
EXPECT_FALSE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
|
||||
EXPECT_FALSE(data.at(JSON_ENCRYPT_CONFIG)
|
||||
.at(JSON_ENCRYPTION_TOKEN)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
EXPECT_FALSE(data.at(JSON_REMOTE_MOUNT)
|
||||
.at(JSON_ENCRYPTION_TOKEN)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
|
||||
clean_json_config(cfg.get_provider_type(), data);
|
||||
EXPECT_TRUE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
|
||||
EXPECT_TRUE(data.at(JSON_ENCRYPT_CONFIG)
|
||||
.at(JSON_ENCRYPTION_TOKEN)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
EXPECT_TRUE(data.at(JSON_REMOTE_MOUNT)
|
||||
.at(JSON_ENCRYPTION_TOKEN)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
}
|
||||
|
||||
TEST(clean_json_test, can_clean_remote_config) {
|
||||
auto dir =
|
||||
utils::path::combine(test::get_test_output_dir(), {
|
||||
"clean_json_test",
|
||||
"remote",
|
||||
});
|
||||
app_config cfg(provider_type::remote, dir);
|
||||
cfg.set_value_by_name(JSON_API_PASSWORD, "moose");
|
||||
cfg.set_value_by_name(
|
||||
fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_ENCRYPTION_TOKEN), "moose");
|
||||
|
||||
auto data = cfg.get_json();
|
||||
EXPECT_FALSE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
|
||||
EXPECT_FALSE(data.at(JSON_REMOTE_CONFIG)
|
||||
.at(JSON_ENCRYPTION_TOKEN)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
|
||||
clean_json_config(cfg.get_provider_type(), data);
|
||||
EXPECT_TRUE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
|
||||
EXPECT_TRUE(data.at(JSON_REMOTE_CONFIG)
|
||||
.at(JSON_ENCRYPTION_TOKEN)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
}
|
||||
|
||||
TEST(clean_json_test, can_clean_s3_config) {
|
||||
auto dir =
|
||||
utils::path::combine(test::get_test_output_dir(), {
|
||||
"clean_json_test",
|
||||
"s3",
|
||||
});
|
||||
app_config cfg(provider_type::s3, dir);
|
||||
cfg.set_value_by_name(JSON_API_PASSWORD, "moose");
|
||||
cfg.set_value_by_name(
|
||||
fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN), "moose");
|
||||
cfg.set_value_by_name(
|
||||
fmt::format("{}.{}", JSON_S3_CONFIG, JSON_ENCRYPTION_TOKEN), "moose");
|
||||
cfg.set_value_by_name(fmt::format("{}.{}", JSON_S3_CONFIG, JSON_SECRET_KEY),
|
||||
"moose");
|
||||
|
||||
auto data = cfg.get_json();
|
||||
EXPECT_FALSE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
|
||||
EXPECT_FALSE(data.at(JSON_REMOTE_MOUNT)
|
||||
.at(JSON_ENCRYPTION_TOKEN)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
EXPECT_FALSE(data.at(JSON_S3_CONFIG)
|
||||
.at(JSON_ENCRYPTION_TOKEN)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
EXPECT_FALSE(
|
||||
data.at(JSON_S3_CONFIG).at(JSON_SECRET_KEY).get<std::string>().empty());
|
||||
|
||||
clean_json_config(cfg.get_provider_type(), data);
|
||||
EXPECT_TRUE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
|
||||
EXPECT_TRUE(data.at(JSON_REMOTE_MOUNT)
|
||||
.at(JSON_ENCRYPTION_TOKEN)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
EXPECT_TRUE(data.at(JSON_S3_CONFIG)
|
||||
.at(JSON_ENCRYPTION_TOKEN)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
EXPECT_TRUE(
|
||||
data.at(JSON_S3_CONFIG).at(JSON_SECRET_KEY).get<std::string>().empty());
|
||||
}
|
||||
|
||||
TEST(clean_json_test, can_clean_sia_config) {
|
||||
auto dir =
|
||||
utils::path::combine(test::get_test_output_dir(), {
|
||||
"clean_json_test",
|
||||
"sia",
|
||||
});
|
||||
app_config cfg(provider_type::sia, dir);
|
||||
cfg.set_value_by_name(JSON_API_PASSWORD, "moose");
|
||||
cfg.set_value_by_name(
|
||||
fmt::format("{}.{}", JSON_HOST_CONFIG, JSON_API_PASSWORD), "moose");
|
||||
cfg.set_value_by_name(
|
||||
fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN), "moose");
|
||||
|
||||
auto data = cfg.get_json();
|
||||
EXPECT_FALSE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
|
||||
EXPECT_FALSE(data.at(JSON_HOST_CONFIG)
|
||||
.at(JSON_API_PASSWORD)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
EXPECT_FALSE(data.at(JSON_REMOTE_MOUNT)
|
||||
.at(JSON_ENCRYPTION_TOKEN)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
|
||||
clean_json_config(cfg.get_provider_type(), data);
|
||||
EXPECT_TRUE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
|
||||
EXPECT_TRUE(data.at(JSON_HOST_CONFIG)
|
||||
.at(JSON_API_PASSWORD)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
EXPECT_TRUE(data.at(JSON_REMOTE_MOUNT)
|
||||
.at(JSON_ENCRYPTION_TOKEN)
|
||||
.get<std::string>()
|
||||
.empty());
|
||||
}
|
||||
} // namespace repertory
|
@ -1,7 +1,10 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:sodium_libs/sodium_libs.dart';
|
||||
|
||||
typedef Validator = bool Function(String);
|
||||
|
||||
@ -224,13 +227,19 @@ bool validateSettings(
|
||||
}
|
||||
|
||||
Map<String, dynamic> convertAllToString(Map<String, dynamic> settings) {
|
||||
final password = 'test';
|
||||
|
||||
settings.forEach((key, value) {
|
||||
if (value is Map<String, dynamic>) {
|
||||
convertAllToString(value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value is String) {
|
||||
if (key == 'ApiPassword' ||
|
||||
key == 'EncryptionToken' ||
|
||||
key == 'SecretKey') {
|
||||
value = encryptValue(value, password);
|
||||
} else if (value is String) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -239,3 +248,29 @@ Map<String, dynamic> convertAllToString(Map<String, dynamic> settings) {
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
String encryptValue(String value, String password) {
|
||||
final sodium = constants.sodium;
|
||||
if (sodium == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
final keyHash = sodium.crypto.genericHash(
|
||||
outLen: sodium.crypto.aeadXChaCha20Poly1305IETF.keyBytes,
|
||||
message: Uint8List.fromList(password.toCharArray()),
|
||||
);
|
||||
|
||||
debugPrint("key: ${base64Encode(keyHash)}");
|
||||
final crypto = sodium.crypto.aeadXChaCha20Poly1305IETF;
|
||||
|
||||
final nonce = sodium.secureRandom(crypto.nonceBytes).extractBytes();
|
||||
debugPrint("nonce: ${base64Encode(nonce)}");
|
||||
final data = crypto.encrypt(
|
||||
additionalData: Uint8List.fromList('repertory'.toCharArray()),
|
||||
key: SecureKey.fromList(sodium, keyHash),
|
||||
message: Uint8List.fromList(value.toCharArray()),
|
||||
nonce: nonce,
|
||||
);
|
||||
|
||||
return base64Encode(Uint8List.fromList([...nonce, ...data]));
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import 'dart:convert' show jsonDecode;
|
||||
import 'dart:convert' show jsonDecode, jsonEncode;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
@ -28,6 +28,7 @@ class _EditSettingsScreenState extends State<EditSettingsScreen> {
|
||||
}
|
||||
|
||||
return UISettingsWidget(
|
||||
origSettings: jsonDecode(jsonEncode(snapshot.requireData)),
|
||||
settings: snapshot.requireData,
|
||||
showAdvanced: false,
|
||||
);
|
||||
@ -56,7 +57,6 @@ class _EditSettingsScreenState extends State<EditSettingsScreen> {
|
||||
return {};
|
||||
}
|
||||
|
||||
// UISettingsWidget(settings: {}, showAdvanced: false),
|
||||
@override
|
||||
void setState(VoidCallback fn) {
|
||||
if (!mounted) {
|
||||
|
@ -16,8 +16,10 @@ import 'package:settings_ui/settings_ui.dart';
|
||||
class UISettingsWidget extends StatefulWidget {
|
||||
final bool showAdvanced;
|
||||
final Map<String, dynamic> settings;
|
||||
final Map<String, dynamic> origSettings;
|
||||
const UISettingsWidget({
|
||||
super.key,
|
||||
required this.origSettings,
|
||||
required this.settings,
|
||||
required this.showAdvanced,
|
||||
});
|
||||
@ -27,8 +29,6 @@ class UISettingsWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _UISettingsWidgetState extends State<UISettingsWidget> {
|
||||
late Map<String, dynamic> _origSettings;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<SettingsTile> commonSettings = [];
|
||||
@ -100,7 +100,12 @@ class _UISettingsWidgetState extends State<UISettingsWidget> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (!DeepCollectionEquality().equals(widget.settings, _origSettings)) {
|
||||
debugPrint('current: ${jsonEncode(widget.settings)}');
|
||||
debugPrint('orig: ${jsonEncode(widget.origSettings)}');
|
||||
if (!DeepCollectionEquality().equals(
|
||||
widget.settings,
|
||||
widget.origSettings,
|
||||
)) {
|
||||
http
|
||||
.put(
|
||||
Uri.parse(
|
||||
@ -118,12 +123,6 @@ class _UISettingsWidgetState extends State<UISettingsWidget> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_origSettings = jsonDecode(jsonEncode(widget.settings));
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void setState(VoidCallback fn) {
|
||||
if (!mounted) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user