feat: auto-update settings

This commit is contained in:
Ixniy Evonniy 2026-07-01 01:12:58 +03:00
parent ac07317119
commit 38aed01ac1
5 changed files with 146 additions and 23 deletions

BIN
cache.db

Binary file not shown.

View File

@ -110,6 +110,8 @@ dialog_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT
trimmed_url := trim(url_utf8);
if trimmed_url {
mode: string;
port := ifx state.is_test_mode then 10899 else 10801;
update_interval := 3600;
config_filename := ifx state.is_test_mode then "config_test.json" else "config.json";
config_data, read_ok := read_entire_file(config_filename);
if read_ok {
@ -119,13 +121,23 @@ dialog_proc :: (hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT
} else {
mode = copy_string("system_proxy");
}
port_str := get_json_val_field(config_data, "\"_port\"");
if port_str {
val, ok := to_integer(port_str);
if ok port = xx val;
}
interval_str := get_json_val_field(config_data, "\"_update_interval\"");
if interval_str {
val, ok := to_integer(interval_str);
if ok update_interval = xx val;
}
free(config_data);
} else {
mode = copy_string("system_proxy");
}
defer free(mode);
minimal_json := tprint("{\n \"_url\": \"%\",\n \"_mode\": \"%\",\n \"inbounds\": [],\n \"outbounds\": []\n}", trimmed_url, mode);
minimal_json := tprint("{\n \"_url\": \"%\",\n \"_mode\": \"%\",\n \"_port\": %,\n \"_update_interval\": %,\n \"inbounds\": [],\n \"outbounds\": []\n}", trimmed_url, mode, port, update_interval);
write_entire_file(config_filename, minimal_json);
state.url_saved = true;
}
@ -342,6 +354,7 @@ show_config_port_dialog :: (parent_hwnd: HWND, is_test_mode := false) -> bool {
config_data, read_ok := read_entire_file(config_filename);
url := "";
mode := "system_proxy";
update_interval := 3600;
if read_ok {
extracted_url := get_json_string_field(config_data, "\"_url\"");
if extracted_url url = copy_string(extracted_url);
@ -353,12 +366,18 @@ show_config_port_dialog :: (parent_hwnd: HWND, is_test_mode := false) -> bool {
mode = copy_string("system_proxy");
}
updated := set_json_metadata(config_data, url, mode, port);
interval_str := get_json_val_field(config_data, "\"_update_interval\"");
if interval_str {
val, ok := to_integer(interval_str);
if ok update_interval = xx val;
}
updated := set_json_metadata(config_data, url, mode, port, update_interval);
write_entire_file(config_filename, updated);
free(config_data);
free(updated);
} else {
minimal_json := tprint("{\n \"_url\": \"\",\n \"_mode\": \"system_proxy\",\n \"_port\": %,\n \"inbounds\": [],\n \"outbounds\": []\n}", port);
minimal_json := tprint("{\n \"_url\": \"\",\n \"_mode\": \"system_proxy\",\n \"_port\": %,\n \"_update_interval\": %,\n \"inbounds\": [],\n \"outbounds\": []\n}", port, update_interval);
write_entire_file(config_filename, minimal_json);
}
if url free(url);

View File

@ -602,6 +602,18 @@ show_context_menu :: (app: *App_State) {
AppendMenuW(hMenu, MF_STRING, CMD_SET_PORT, utf8_to_wide("Configure Port..."));
AppendMenuW(hMenu, MF_STRING, CMD_UPDATE_NOW, utf8_to_wide("Update Config Now"));
hUpdateMenu := CreatePopupMenu();
current_interval := app.updater_data.update_interval_seconds;
AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 0 then MF_CHECKED else 0), CMD_UPDATE_NEVER, utf8_to_wide("Never"));
AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 1800 then MF_CHECKED else 0), CMD_UPDATE_30M, utf8_to_wide("30 minutes"));
AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 3600 then MF_CHECKED else 0), CMD_UPDATE_1H, utf8_to_wide("1 hour"));
AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 21600 then MF_CHECKED else 0), CMD_UPDATE_6H, utf8_to_wide("6 hours"));
AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 43200 then MF_CHECKED else 0), CMD_UPDATE_12H, utf8_to_wide("12 hours"));
AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 86400 then MF_CHECKED else 0), CMD_UPDATE_DAILY, utf8_to_wide("Daily"));
AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 259200 then MF_CHECKED else 0), CMD_UPDATE_3D, utf8_to_wide("3 days"));
AppendMenuW(hUpdateMenu, MF_STRING | (ifx current_interval == 604800 then MF_CHECKED else 0), CMD_UPDATE_WEEKLY, utf8_to_wide("weekly"));
AppendMenuW(hMenu, MF_POPUP, cast(s64) hUpdateMenu, utf8_to_wide("Config Auto-Update"));
AppendMenuW(hMenu, MF_SEPARATOR, 0, null);
proxy_flags := MF_STRING;
@ -685,7 +697,7 @@ show_context_menu :: (app: *App_State) {
config_data, read_ok := read_entire_file(config_filename);
if read_ok {
url := get_json_string_field(config_data, "\"_url\"");
updated := set_json_metadata(config_data, url, "proxy", app.port);
updated := set_json_metadata(config_data, url, "proxy", app.port, app.updater_data.update_interval_seconds);
write_entire_file(config_filename, updated);
free(config_data);
free(updated);
@ -710,7 +722,7 @@ show_context_menu :: (app: *App_State) {
config_data, read_ok := read_entire_file(config_filename);
if read_ok {
url := get_json_string_field(config_data, "\"_url\"");
updated := set_json_metadata(config_data, url, "system_proxy", app.port);
updated := set_json_metadata(config_data, url, "system_proxy", app.port, app.updater_data.update_interval_seconds);
write_entire_file(config_filename, updated);
free(config_data);
free(updated);
@ -738,6 +750,38 @@ show_context_menu :: (app: *App_State) {
MessageBoxW(app.hwnd, utf8_to_wide("Failed to update Windows registry autostart configuration."), utf8_to_wide("Sing-box Tray"), MB_ICONERROR);
}
}
case CMD_UPDATE_NEVER; {
save_update_interval(app, 0);
log_print("Auto-update interval set to: Never\n");
}
case CMD_UPDATE_30M; {
save_update_interval(app, 1800);
log_print("Auto-update interval set to: 30 minutes\n");
}
case CMD_UPDATE_1H; {
save_update_interval(app, 3600);
log_print("Auto-update interval set to: 1 hour\n");
}
case CMD_UPDATE_6H; {
save_update_interval(app, 21600);
log_print("Auto-update interval set to: 6 hours\n");
}
case CMD_UPDATE_12H; {
save_update_interval(app, 43200);
log_print("Auto-update interval set to: 12 hours\n");
}
case CMD_UPDATE_DAILY; {
save_update_interval(app, 86400);
log_print("Auto-update interval set to: Daily\n");
}
case CMD_UPDATE_3D; {
save_update_interval(app, 259200);
log_print("Auto-update interval set to: 3 days\n");
}
case CMD_UPDATE_WEEKLY; {
save_update_interval(app, 604800);
log_print("Auto-update interval set to: weekly\n");
}
case CMD_EXIT; {
DestroyWindow(app.hwnd);
}
@ -816,6 +860,25 @@ load_stock_icon :: (siid: s32) -> HICON {
global_is_test_mode: bool = false;
save_update_interval :: (app: *App_State, interval: s32) {
app.updater_data.update_interval_seconds = interval;
config_filename := ifx app.is_test_mode then "config_test.json" else "config.json";
config_data, read_ok := read_entire_file(config_filename);
if read_ok {
url := get_json_string_field(config_data, "\"_url\"");
mode := get_json_string_field(config_data, "\"_mode\"");
mode_val := ifx mode then mode else "system_proxy";
updated := set_json_metadata(config_data, url, mode_val, app.port, interval);
write_entire_file(config_filename, updated);
free(config_data);
free(updated);
}
}
main :: () {
args := get_command_line_arguments();
is_test := false;
@ -891,7 +954,9 @@ main :: () {
config_filename := ifx app_state.is_test_mode then "config_test.json" else "config.json";
// Load persisted mode and port from config file
app_state.updater_data.update_interval_seconds = 3600; // default
// Load persisted mode, port, and update interval from config file
config_data, read_ok := read_entire_file(config_filename);
if read_ok {
extracted_mode := get_json_string_field(config_data, "\"_mode\"");
@ -899,6 +964,15 @@ main :: () {
app_state.use_system_proxy = false;
}
interval_str := get_json_val_field(config_data, "\"_update_interval\"");
if interval_str {
val, ok := to_integer(interval_str);
if ok {
app_state.updater_data.update_interval_seconds = xx val;
log_print("Loaded update interval: % seconds\n", app_state.updater_data.update_interval_seconds);
}
}
if app_state.is_test_mode {
app_state.port = 10899; // force test port in test mode
log_info("Loaded config from %. Test mode: forcing port to %.\n", config_filename, app_state.port);
@ -946,7 +1020,6 @@ main :: () {
defer KillTimer(hwnd, TIMER_PROCESS_CHECK);
app_state.updater_data.hwnd = hwnd;
app_state.updater_data.update_interval_seconds = 3600;
app_state.updater_data.stop_event = CreateEventW(null, TRUE, FALSE, null);
app_state.updater_data.is_test_mode = app_state.is_test_mode;

View File

@ -110,6 +110,15 @@ perform_update :: (is_test_mode := false) -> changed: bool, success: bool, error
}
}
update_interval := 3600;
interval_str := get_json_val_field(config_data, "\"_update_interval\"");
if interval_str {
val, parse_ok, remainder := to_integer(interval_str);
if parse_ok {
update_interval = xx val;
}
}
downloaded, success, err_msg := download_url(url);
if !success return false, false, err_msg;
defer free(downloaded);
@ -117,7 +126,7 @@ perform_update :: (is_test_mode := false) -> changed: bool, success: bool, error
modified := modify_config_inbounds(downloaded, port);
defer free(modified);
final_config := set_json_metadata(modified, url, mode, port);
final_config := set_json_metadata(modified, url, mode, port, update_interval);
defer free(final_config);
if config_data == final_config {
@ -137,14 +146,23 @@ updater_thread_proc :: (thread: *Thread) -> s64 {
data := cast(*Updater_Thread_Data) thread.data;
if !data return 1;
elapsed_seconds := 0;
while true {
// Wait on the stop event for the specified interval.
// If the stop event is signaled, wake up immediately and terminate.
status := WaitForSingleObject(data.stop_event, cast(DWORD) (data.update_interval_seconds * 1000));
status := WaitForSingleObject(data.stop_event, 1000);
if status == WAIT_OBJECT_0 {
break;
}
interval := data.update_interval_seconds;
if interval <= 0 {
elapsed_seconds = 0;
continue;
}
elapsed_seconds += 1;
if elapsed_seconds >= interval {
elapsed_seconds = 0;
log_debug("Background update: Starting automatic config check...\n");
changed, success, err_msg := perform_update(data.is_test_mode);
if success {
@ -160,6 +178,7 @@ updater_thread_proc :: (thread: *Thread) -> s64 {
log_error("Background update failed: %\n", err_msg);
}
}
}
return 0;
}
@ -277,7 +296,7 @@ get_json_val_field :: (json: string, field: string) -> string {
return trim(slice(json, val_start, val_end - val_start + 1));
}
set_json_metadata :: (json: string, url: string, mode: string, port: int) -> string {
set_json_metadata :: (json: string, url: string, mode: string, port: int, update_interval: int) -> string {
lines := split(json, "\n");
defer array_free(lines);
@ -289,12 +308,13 @@ set_json_metadata :: (json: string, url: string, mode: string, port: int) -> str
if find_index_from_left(trimmed, "\"_url\"") != -1 continue;
if find_index_from_left(trimmed, "\"_mode\"") != -1 continue;
if find_index_from_left(trimmed, "\"_port\"") != -1 continue;
if find_index_from_left(trimmed, "\"_update_interval\"") != -1 continue;
append(*builder, it);
append(*builder, "\n");
if !inserted && find_index_from_left(trimmed, "{") != -1 {
append(*builder, tprint(" \"_url\": \"%\",\n \"_mode\": \"%\",\n \"_port\": %,\n", url, mode, port));
append(*builder, tprint(" \"_url\": \"%\",\n \"_mode\": \"%\",\n \"_port\": %,\n \"_update_interval\": %,\n", url, mode, port, update_interval));
inserted = true;
}
}
@ -312,6 +332,7 @@ strip_json_metadata :: (json: string) -> string {
if find_index_from_left(trimmed, "\"_url\"") != -1 continue;
if find_index_from_left(trimmed, "\"_mode\"") != -1 continue;
if find_index_from_left(trimmed, "\"_port\"") != -1 continue;
if find_index_from_left(trimmed, "\"_update_interval\"") != -1 continue;
append(*builder, it);
append(*builder, "\n");

View File

@ -46,7 +46,9 @@ MF_GRAYED : u32 : 0x00000001;
MF_DISABLED : u32 : 0x00000002;
MF_SEPARATOR : u32 : 0x00000800;
MF_CHECKED : u32 : 0x00000008;
MF_POPUP : u32 : 0x00000010;
// NIM constants
NIM_ADD : u32 : 0x00000000;
NIM_MODIFY : u32 : 0x00000001;
NIM_DELETE : u32 : 0x00000002;
@ -83,6 +85,14 @@ CMD_MODE_PROXY :: 1006;
CMD_MODE_SYS_PROXY :: 1007;
CMD_SET_PORT :: 1008;
CMD_TOGGLE_AUTOSTART:: 1009;
CMD_UPDATE_NEVER :: 1010;
CMD_UPDATE_30M :: 1011;
CMD_UPDATE_1H :: 1012;
CMD_UPDATE_6H :: 1013;
CMD_UPDATE_12H :: 1014;
CMD_UPDATE_DAILY :: 1015;
CMD_UPDATE_3D :: 1016;
CMD_UPDATE_WEEKLY :: 1017;
// Boolean constants
FALSE :: 0;