You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
237 lines
11 KiB
237 lines
11 KiB
/* |
|
* chat_core.h — центральный API чата в потоке uasync |
|
* |
|
* Все DB-операции и сетевые функции выполняются в uasync-потоке. |
|
* GUI вызывает chat_core_submit_message() через gui_bridge_post_uasync(). |
|
*/ |
|
|
|
#ifndef CHAT_CORE_H |
|
#define CHAT_CORE_H |
|
|
|
#include <stdint.h> |
|
#include <stddef.h> |
|
#include <sqlite3.h> |
|
|
|
struct sqlite3; |
|
struct UTUN_INSTANCE; |
|
|
|
/* ── Жизненный цикл ── */ |
|
|
|
int chat_core_init(struct UTUN_INSTANCE* inst, const char* db_path); |
|
void chat_core_destroy(struct UTUN_INSTANCE* inst); |
|
|
|
/* Backfill: регистрирует уже скачанные медиафайлы (лежат на диске, но отсутствуют |
|
* в media_files) — вызывается после загрузки каналов/сообщений при старте. */ |
|
void chat_media_backfill(void); |
|
|
|
/* Докачка недостающего медиа за последние storage_backfill_days дней. Проходит по |
|
* медиасообщениям каналов и запускает загрузку для тех, чей файл отсутствует на диске. */ |
|
void chat_media_autodownload_backfill(void); |
|
|
|
/* Регистрирует триггер: после первого SYNC_DONE (+ дебаунс 2с) анонсирует локальные |
|
* блоки суперузлам и запускает chat_media_autodownload_backfill. Один раз за процесс. */ |
|
void chat_media_startup_backfill_init(struct UTUN_INSTANCE* inst); |
|
void chat_media_startup_backfill_destroy(struct UTUN_INSTANCE* inst); |
|
|
|
struct sqlite3* chat_core_get_db(void); |
|
struct UTUN_INSTANCE* chat_core_get_inst(void); |
|
int chat_core_is_initialized(void); |
|
|
|
/* ── Отправка сообщения (GUI → uasync) ── */ |
|
|
|
struct chat_msg_submit { |
|
char channel_id[64]; |
|
char content_type[32]; |
|
char media_src[1024]; |
|
char media_dest[1024]; |
|
uint8_t media_copy; |
|
uint8_t media_video; /* 1 = подготовить как видео (пробинг+транскод) */ |
|
int duration_ms; /* видео: длительность (0 = неизвестно) */ |
|
int width; /* видео: ширина */ |
|
int height; /* видео: высота */ |
|
uint8_t* data; |
|
uint32_t data_len; |
|
uint64_t timestamp; |
|
uint64_t reply_to_ts; /* 0 = не ответ */ |
|
uint64_t reply_to_node_id; /* node_id автора исходного сообщения */ |
|
}; |
|
|
|
void chat_core_submit_message(struct chat_msg_submit* req); |
|
|
|
/* Отправка медиа-сообщения (вложение): src->dest (copy) или видео (transcode). */ |
|
void chat_core_submit_media_message(struct chat_msg_submit* req); |
|
|
|
/* ── DB-операции (оставлены для интроспекции) ── */ |
|
|
|
uint32_t chat_core_count(const char* ch_id); |
|
int chat_core_chain_hash_at(const char* ch_id, uint32_t pos, uint8_t* hash_out); |
|
int chat_core_list_channels(uint8_t* buf, size_t buf_size, size_t* out_len); |
|
int chat_core_list_peers(const char* ch_id, uint8_t* buf, size_t buf_size, size_t* out_len); |
|
int chat_core_load_nodeinfo(uint64_t node_id, uint8_t* buf, size_t buf_size, size_t* out_len); |
|
|
|
/* ── Создание канала (GUI → uasync) ── */ |
|
|
|
struct chat_channel_create { |
|
char channel_id[64]; |
|
char name[128]; |
|
uint64_t owner_node_id; |
|
uint8_t x25519_pubkey[32]; |
|
uint8_t x25519_privkey[32]; |
|
uint8_t ed25519_pubkey[32]; |
|
uint8_t ed25519_privkey[32]; |
|
uint8_t signature[64]; |
|
}; |
|
|
|
void chat_core_ensure_channel_ready(const char* ch_id); |
|
void chat_core_create_channel(struct chat_channel_create* req); |
|
void chat_core_create_channel_trampoline(void* arg); |
|
void chat_core_create_channel_auto(const char* name); |
|
void chat_core_create_channel_auto_trampoline(void* arg); |
|
|
|
/* ── Локальное удаление канала (GUI → uasync) ── */ |
|
|
|
struct chat_delete_channel_req { |
|
char ch_id[64]; |
|
}; |
|
|
|
/* Удалить канал с этого устройства: отмена медиа-загрузок, удаление группы |
|
* (соединения/синк), db_sync инстанса, таблиц msg_ и peers_, медиафайлов и |
|
* merkle-хешей. Работает в uasync-потоке. Другие узлы канал не теряют. */ |
|
void chat_core_delete_channel(const char* ch_id); |
|
void chat_core_delete_channel_trampoline(void* arg); |
|
|
|
/* ── Утилиты ── */ |
|
|
|
void chat_core_set_my_node_id(uint64_t node_id); |
|
void chat_core_update_my_name(const char* name); |
|
void chat_core_update_my_name_trampoline(void* arg); |
|
void chat_core_sync_my_addresses(void); |
|
/* Единое обновление своего мембера: адреса + bump update_ts + подпись (с адресами) + синк */ |
|
void chat_core_update_my_member(void); |
|
|
|
/* Сохранение key-value в ui_state (вызывается из uasync-потока) */ |
|
void chat_core_save_ui_state(const char* key, const char* value); |
|
void chat_core_save_ui_state_trampoline(void* arg); |
|
|
|
/* Установка chat-настройки (name=value) через chat_setting API (GUI → uasync) */ |
|
void chat_core_set_setting(const char* name, const char* value); |
|
void chat_core_set_setting_trampoline(void* arg); |
|
|
|
/* Запуск подключения к узлам выбранного канала (GUI → uasync) */ |
|
void chat_core_connect_channel(const char* ch_id); |
|
void chat_core_connect_channel_trampoline(void* arg); |
|
|
|
/* Однократная попытка подключения к конкретному узлу канала (GUI → uasync) */ |
|
void chat_core_connect_node(const char* ch_id, uint64_t node_id); |
|
void chat_core_connect_node_trampoline(void* arg); |
|
|
|
struct chat_connect_node_req { |
|
char ch_id[64]; |
|
uint64_t node_id; |
|
}; |
|
|
|
/* Трамплин для gui_bridge_post_uasync (GUI → uasync) */ |
|
void chat_core_submit_trampoline(void* arg); |
|
|
|
/* Трамплин для ручного запуска загрузки медиа (JNI → uasync) */ |
|
struct attachment_dl_req { |
|
char channel_id[64]; |
|
int64_t msg_id; |
|
}; |
|
void chat_core_attachment_download_trampoline(void* arg); |
|
|
|
/* Обновление local_attrs для записи (вызывается из uasync-потока) */ |
|
int chat_core_update_local_attrs(const char* ch_id, uint64_t ts, |
|
const uint8_t* author_sig, |
|
const char* local_attrs_json); |
|
|
|
/* Пометить голосовое сообщение как проигранное (добавляет "pl":1 в local_attrs) */ |
|
void chat_core_mark_voice_played(const char* channel_id, uint64_t ts, |
|
uint64_t author_node_id); |
|
void chat_core_mark_voice_played_trampoline(void* arg); |
|
|
|
struct voice_played_req { |
|
char channel_id[64]; |
|
uint64_t ts; |
|
uint64_t author_node_id; |
|
}; |
|
|
|
/* Сбор статуса (NTP + connections) и отправка в GUI */ |
|
void chat_core_collect_status_trampoline(void* arg); |
|
|
|
/* Сбор списка ETCP-соединений и отправка в GUI (binary packet) */ |
|
void chat_core_collect_conn_list_trampoline(void* arg); |
|
|
|
/* Сбор полных метрик одного соединения (arg = u_malloc uint64_t peer_node_id) */ |
|
void chat_core_collect_conn_metrics_trampoline(void* arg); |
|
|
|
/* Сбор live-снапшота для панели деталей участника (arg = u_malloc uint64_t node_id) */ |
|
void chat_core_collect_member_detail_trampoline(void* arg); |
|
|
|
/* ── Headless CLI API ── */ |
|
|
|
/** Вариант для сборки без UTUN_INSTANCE (использует sqlite3* и node_id) */ |
|
void chat_core_set_ctx_for_headless(sqlite3* db, struct UTUN_INSTANCE* inst, uint64_t my_node_id); |
|
|
|
/** Список каналов с метаданными в JSON: [{id,name,owner_id,owner_name,peers,msgs,online}] */ |
|
int chat_core_get_channels_json(char* buf, size_t buf_size, size_t* out_len); |
|
|
|
/** Сообщения канала в JSON: [{id,ts,author_id,author_name,content_type,data}] |
|
* count=0 → все, offset=0 → сначала */ |
|
int chat_core_get_messages_json(const char* ch_id, int count, int offset, |
|
char* buf, size_t buf_size, size_t* out_len); |
|
|
|
/** Мемберы канала с полным состоянием в JSON: |
|
* [{node_id,name,online,connected,x25519,ed25519,addrs:[{ip,port,proto,rtt}]}] */ |
|
int chat_core_get_members_json(const char* ch_id, char* buf, size_t buf_size, size_t* out_len); |
|
|
|
/** Имя узла из таблицы nodes */ |
|
int chat_core_get_node_name(uint64_t node_id, char* out, size_t sz); |
|
|
|
/* ── Редактирование adm_tags (владелец канала) ── */ |
|
|
|
#define CHAT_MEMBER_TAGS_MAX_KEYS 16 |
|
#define CHAT_MEMBER_TAGS_KEY_SZ 64 |
|
#define CHAT_MEMBER_TAGS_VAL_SZ 256 |
|
|
|
struct chat_member_tags; |
|
|
|
/** Загрузить текущие adm_tags мембера в память. Возвращает NULL если мембера нет. */ |
|
struct chat_member_tags* chat_member_tags_load(const char* ch_id, uint64_t node_id); |
|
|
|
/** Установить ключ. val=NULL → удалить ключ. ver — особый, автоинкремент при commit. */ |
|
int chat_member_tags_set(struct chat_member_tags* t, const char* key, const char* val); |
|
|
|
/** Получить значение ключа. Возвращает NULL если ключ не найден. */ |
|
const char* chat_member_tags_get(struct chat_member_tags* t, const char* key); |
|
|
|
/** Заменить все теги данными из JSON-строки (плоский, строковые значения). */ |
|
int chat_member_tags_replace(struct chat_member_tags* t, const char* json); |
|
|
|
/** Сохранить: сериализовать в JSON, инкрементировать ver, подписать канальным Ed25519, |
|
* записать через member_sync_put() → синхронизируется всем. */ |
|
int chat_member_tags_commit(struct chat_member_tags* t); |
|
|
|
/** Освободить без сохранения. */ |
|
void chat_member_tags_free(struct chat_member_tags* t); |
|
|
|
/** Трамплин для gui_bridge: arg = struct chat_member_tags_req* (доверенная память). */ |
|
struct chat_member_tags_req { |
|
char ch_id[64]; |
|
uint64_t node_id; |
|
char json[512]; |
|
}; |
|
void chat_member_tags_commit_trampoline(void* arg); |
|
|
|
/* ── Передача прав админа (владелец → другому узлу) ── */ |
|
|
|
/** Передать оба канальных privkey выбранному узлу (encrypted+signed через etcp_router). */ |
|
void chat_core_transfer_admin(const char* ch_id, uint64_t target_node_id); |
|
void chat_core_transfer_admin_trampoline(void* arg); |
|
|
|
struct chat_transfer_admin_req { |
|
char ch_id[64]; |
|
uint64_t target_node_id; |
|
}; |
|
|
|
#endif /* CHAT_CORE_H */
|
|
|