mirror of
https://github.com/simon987/sist2.git
synced 2025-04-16 08:56:45 +00:00
wip
This commit is contained in:
parent
230988d6d1
commit
8127745f2b
@ -326,7 +326,7 @@ int web_args_validate(web_args_t *args, int argc, const char **argv) {
|
||||
}
|
||||
|
||||
strncpy(args->auth_user, args->credentials, (ptr - args->credentials));
|
||||
strncpy(args->auth_pass, ptr + 1, strlen(ptr + 1));
|
||||
strcpy(args->auth_pass, ptr);
|
||||
|
||||
if (strlen(args->auth_user) == 0) {
|
||||
fprintf(stderr, "--auth username must be at least one character long");
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "libscan/text/text.h"
|
||||
#include "libscan/mobi/scan_mobi.h"
|
||||
#include "libscan/raw/raw.h"
|
||||
#include "src/io/store.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <pcre.h>
|
||||
|
@ -313,6 +313,11 @@ void finish_indexer(char *script, char *index_id) {
|
||||
r = web_post(url, "");
|
||||
LOG_INFOF("elastic.c", "Merge index <%d>", r->status_code);
|
||||
free_response(r);
|
||||
|
||||
snprintf(url, sizeof(url), "%s/sist2/_settings", IndexCtx.es_url);
|
||||
r = web_put(url, "{\"index\":{\"refresh_interval\":\"1s\"}}");
|
||||
LOG_INFOF("elastic.c", "Set refresh interval <%d>", r->status_code);
|
||||
free_response(r);
|
||||
}
|
||||
|
||||
void elastic_init(int force_reset) {
|
||||
|
@ -1,9 +1,10 @@
|
||||
#include "store.h"
|
||||
#include "src/ctx.h"
|
||||
|
||||
store_t *store_create(char *path) {
|
||||
store_t *store_create(char *path, size_t chunk_size) {
|
||||
|
||||
store_t *store = malloc(sizeof(struct store_t));
|
||||
store->chunk_size = chunk_size;
|
||||
pthread_rwlock_init(&store->lock, NULL);
|
||||
|
||||
mdb_env_create(&store->env);
|
||||
@ -18,7 +19,7 @@ store_t *store_create(char *path) {
|
||||
LOG_FATALF("store.c", "Error while opening store: %s (%s)\n", mdb_strerror(open_ret), path)
|
||||
}
|
||||
|
||||
store->size = (size_t) 1024 * 1024 * 5;
|
||||
store->size = (size_t) store->chunk_size;
|
||||
ScanCtx.stat_tn_size = 0;
|
||||
mdb_env_set_mapsize(store->env, store->size);
|
||||
|
||||
@ -69,7 +70,7 @@ void store_write(store_t *store, char *key, size_t key_len, char *buf, size_t bu
|
||||
// Cannot resize when there is a opened transaction.
|
||||
// Resize take effect on the next commit.
|
||||
pthread_rwlock_wrlock(&store->lock);
|
||||
store->size += 1024 * 1024 * 50;
|
||||
store->size += store->chunk_size;
|
||||
mdb_env_set_mapsize(store->env, store->size);
|
||||
mdb_txn_begin(store->env, NULL, 0, &txn);
|
||||
put_ret = mdb_put(txn, store->dbi, &mdb_key, &mdb_value, 0);
|
||||
|
@ -4,14 +4,18 @@
|
||||
#include <pthread.h>
|
||||
#include <lmdb.h>
|
||||
|
||||
#define STORE_SIZE_TN 1024 * 1024 * 5
|
||||
#define STORE_SIZE_TAG 1024 * 16
|
||||
|
||||
typedef struct store_t {
|
||||
MDB_dbi dbi;
|
||||
MDB_env *env;
|
||||
size_t size;
|
||||
size_t chunk_size;
|
||||
pthread_rwlock_t lock;
|
||||
} store_t;
|
||||
|
||||
store_t *store_create(char *path);
|
||||
store_t *store_create(char *path, size_t chunk_size);
|
||||
|
||||
void store_destroy(store_t *store);
|
||||
|
||||
|
10
src/main.c
10
src/main.c
@ -176,7 +176,7 @@ void sist2_scan(scan_args_t *args) {
|
||||
char store_path[PATH_MAX];
|
||||
snprintf(store_path, PATH_MAX, "%sthumbs", ScanCtx.index.path);
|
||||
mkdir(store_path, S_IWUSR | S_IRUSR | S_IXUSR);
|
||||
ScanCtx.index.store = store_create(store_path);
|
||||
ScanCtx.index.store = store_create(store_path, STORE_SIZE_TN);
|
||||
|
||||
scan_print_header();
|
||||
|
||||
@ -223,7 +223,7 @@ void sist2_scan(scan_args_t *args) {
|
||||
char dst_path[PATH_MAX];
|
||||
snprintf(store_path, PATH_MAX, "%sthumbs", args->incremental);
|
||||
snprintf(dst_path, PATH_MAX, "%s_index_original", ScanCtx.index.path);
|
||||
store_t *source = store_create(store_path);
|
||||
store_t *source = store_create(store_path, STORE_SIZE_TN);
|
||||
|
||||
DIR *dir = opendir(args->incremental);
|
||||
if (dir == NULL) {
|
||||
@ -339,7 +339,11 @@ void sist2_web(web_args_t *args) {
|
||||
char path_tmp[PATH_MAX];
|
||||
|
||||
snprintf(path_tmp, PATH_MAX, "%sthumbs", abs_path);
|
||||
WebCtx.indices[i].store = store_create(path_tmp);
|
||||
WebCtx.indices[i].store = store_create(path_tmp, STORE_SIZE_TN);
|
||||
|
||||
snprintf(path_tmp, PATH_MAX, "%stags", abs_path);
|
||||
mkdir(path_tmp, S_IWUSR | S_IRUSR | S_IXUSR);
|
||||
WebCtx.indices[i].tag_store = store_create(path_tmp, STORE_SIZE_TAG);
|
||||
|
||||
snprintf(path_tmp, PATH_MAX, "%sdescriptor.json", abs_path);
|
||||
WebCtx.indices[i].desc = read_index_descriptor(path_tmp);
|
||||
|
@ -106,6 +106,21 @@ body {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.badge {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.badge-delete {
|
||||
margin-right: -2px;
|
||||
margin-left: 2px;
|
||||
font-family: monospace;
|
||||
font-size: 90%;
|
||||
background: rgba(0,0,0,0.2);
|
||||
padding: 0.1em 0.4em;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.badge-text {
|
||||
color: #FFFFFF;
|
||||
background-color: #FAAB3C;
|
||||
@ -137,9 +152,6 @@ body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.badge {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.fit {
|
||||
display: block;
|
||||
|
@ -153,26 +153,43 @@ function getTags(hit, mimeCategory) {
|
||||
// User tags
|
||||
if (hit["_source"].hasOwnProperty("tag")) {
|
||||
hit["_source"]["tag"].forEach(tag => {
|
||||
const userTag = document.createElement("span");
|
||||
userTag.setAttribute("class", "badge badge-pill badge-user");
|
||||
|
||||
const tokens = tag.split("#");
|
||||
|
||||
if (tokens.length > 1) {
|
||||
const bg = "#" + tokens[1];
|
||||
const fg = lum(tokens[1]) > 50 ? "#000" : "#fff";
|
||||
userTag.setAttribute("style", `background-color: ${bg}; color: ${fg}`);
|
||||
}
|
||||
|
||||
const name = tokens[0].split(".")[tokens[0].split(".").length - 1];
|
||||
userTag.appendChild(document.createTextNode(name));
|
||||
tags.push(userTag);
|
||||
tags.push(makeUserTag(tag, hit));
|
||||
})
|
||||
}
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
function makeUserTag(tag, hit) {
|
||||
const userTag = document.createElement("span");
|
||||
userTag.setAttribute("class", "badge badge-pill badge-user");
|
||||
|
||||
const tokens = tag.split("#");
|
||||
|
||||
if (tokens.length > 1) {
|
||||
const bg = "#" + tokens[1];
|
||||
const fg = lum(tokens[1]) > 50 ? "#000" : "#fff";
|
||||
userTag.setAttribute("style", `background-color: ${bg}; color: ${fg}`);
|
||||
}
|
||||
|
||||
const deleteButton = document.createElement("span");
|
||||
deleteButton.setAttribute("class", "badge badge-pill badge-delete")
|
||||
deleteButton.setAttribute("title", "Delete tag")
|
||||
deleteButton.appendChild(document.createTextNode("X"));
|
||||
deleteButton.addEventListener("click", () => {
|
||||
deleteTag(tag, hit).then(() => {
|
||||
userTag.remove();
|
||||
});
|
||||
});
|
||||
userTag.addEventListener("mouseenter", () => userTag.appendChild(deleteButton));
|
||||
userTag.addEventListener("mouseleave", () => deleteButton.remove());
|
||||
|
||||
const name = tokens[0].split(".")[tokens[0].split(".").length - 1];
|
||||
userTag.appendChild(document.createTextNode(name));
|
||||
|
||||
return userTag;
|
||||
}
|
||||
|
||||
function infoButtonCb(hit) {
|
||||
return () => {
|
||||
getDocumentInfo(hit["_id"]).then(doc => {
|
||||
@ -338,11 +355,11 @@ function createDocCard(hit) {
|
||||
|
||||
docCardBody.appendChild(tagContainer);
|
||||
|
||||
attachTagContainerEventListener(tagContainer);
|
||||
attachTagContainerEventListener(tagContainer, hit);
|
||||
return docCard;
|
||||
}
|
||||
|
||||
function attachTagContainerEventListener(tagContainer) {
|
||||
function attachTagContainerEventListener(tagContainer, hit) {
|
||||
const sizeTag = Array.from(tagContainer.children).find(child => child.tagName === "SMALL");
|
||||
|
||||
const addTagButton = document.createElement("span");
|
||||
@ -354,6 +371,10 @@ function attachTagContainerEventListener(tagContainer) {
|
||||
|
||||
addTagButton.addEventListener("click", () => {
|
||||
tagBar.value = "";
|
||||
currentDocToTag = hit;
|
||||
currentTagCallback = tag => {
|
||||
tagContainer.insertBefore(makeUserTag(tag, hit), sizeTag);
|
||||
}
|
||||
$("#tagModal").modal("show");
|
||||
tagBar.focus();
|
||||
});
|
||||
@ -431,7 +452,6 @@ function createDocLine(hit) {
|
||||
|
||||
if (hit["_source"].hasOwnProperty("parent")) {
|
||||
line.classList.add("sub-document");
|
||||
isSubDocument = true;
|
||||
}
|
||||
|
||||
const infoButton = makeInfoButton(hit);
|
||||
@ -504,6 +524,8 @@ function createDocLine(hit) {
|
||||
pathLine.appendChild(path);
|
||||
pathLine.appendChild(tagContainer);
|
||||
|
||||
attachTagContainerEventListener(tagContainer, hit);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@ let tagTree;
|
||||
let searchBar = document.getElementById("searchBar");
|
||||
let pathBar = document.getElementById("pathBar");
|
||||
let tagBar = document.getElementById("tagBar");
|
||||
let currentDocToTag = null;
|
||||
let currentTagCallback = null;
|
||||
let lastDoc = null;
|
||||
let reachedEnd = false;
|
||||
let docCount = 0;
|
||||
@ -136,8 +138,8 @@ window.onload = () => {
|
||||
[tagBar, document.getElementById("tag-color")].forEach(elem => {
|
||||
elem.addEventListener("keyup", e => {
|
||||
if (e.key === "Enter") {
|
||||
console.log("Add tag");
|
||||
//TODO
|
||||
const tag = tagBar.value + document.getElementById("tag-color").value;
|
||||
saveTag(tag, currentDocToTag).then(() => currentTagCallback(tag));
|
||||
}
|
||||
});
|
||||
})
|
||||
@ -159,11 +161,57 @@ window.onload = () => {
|
||||
});
|
||||
};
|
||||
|
||||
function saveTag(tag, hit) {
|
||||
const relPath = hit["_source"]["path"] + "/" + hit["_source"] + ext(hit);
|
||||
|
||||
return $.jsonPost("/tag/" + hit["_source"]["index"], {
|
||||
delete: false,
|
||||
name: tag,
|
||||
doc_id: hit["_id"],
|
||||
relpath: relPath
|
||||
}).then(() => {
|
||||
tagBar.blur();
|
||||
$("#tagModal").modal("hide");
|
||||
$.toast({
|
||||
heading: "Tag added",
|
||||
text: "Tag saved to index storage and updated in ElasticSearch",
|
||||
stack: 3,
|
||||
bgColor: "#00a4bc",
|
||||
textColor: "#fff",
|
||||
position: 'bottom-right',
|
||||
hideAfter: 3000,
|
||||
loaderBg: "#08c7e8",
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function deleteTag(tag, hit) {
|
||||
const relPath = hit["_source"]["path"] + "/" + hit["_source"] + ext(hit);
|
||||
|
||||
return $.jsonPost("/tag/" + hit["_source"]["index"], {
|
||||
delete: true,
|
||||
name: tag,
|
||||
doc_id: hit["_id"],
|
||||
relpath: relPath
|
||||
}).then(() => {
|
||||
$.toast({
|
||||
heading: "Tag deleted",
|
||||
text: "Tag deleted index storage and updated in ElasticSearch",
|
||||
stack: 3,
|
||||
bgColor: "#00a4bc",
|
||||
textColor: "#fff",
|
||||
position: 'bottom-right',
|
||||
hideAfter: 3000,
|
||||
loaderBg: "#08c7e8",
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function toggleFuzzy() {
|
||||
searchDebounced();
|
||||
}
|
||||
|
||||
$.jsonPost("i").then(resp => {
|
||||
$.get("i").then(resp => {
|
||||
|
||||
const urlIndices = (new URLSearchParams(location.search)).get("i");
|
||||
resp["indices"].forEach(idx => {
|
||||
|
@ -19,6 +19,7 @@ typedef struct index_descriptor {
|
||||
typedef struct index_t {
|
||||
struct index_descriptor desc;
|
||||
struct store_t *store;
|
||||
struct store_t *tag_store;
|
||||
char path[PATH_MAX];
|
||||
} index_t;
|
||||
|
||||
|
166
src/web/serve.c
166
src/web/serve.c
@ -53,6 +53,14 @@ store_t *get_store(const char *index_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
store_t *get_tag_store(const char *index_id) {
|
||||
index_t *idx = get_index_by_id(index_id);
|
||||
if (idx != NULL) {
|
||||
return idx->tag_store;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void search_index(struct mg_connection *nc) {
|
||||
send_response_line(nc, 200, sizeof(search_html), "Content-Type: text/html");
|
||||
mg_send(nc, search_html, sizeof(search_html));
|
||||
@ -422,6 +430,162 @@ void status(struct mg_connection *nc) {
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int delete;
|
||||
char *relpath;
|
||||
char *doc_id;
|
||||
} tag_req_t;
|
||||
|
||||
tag_req_t *parse_tag_request(cJSON *json) {
|
||||
|
||||
if (!cJSON_IsObject(json)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON *arg_name = cJSON_GetObjectItem(json, "name");
|
||||
if (arg_name == NULL || !cJSON_IsString(arg_name)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON *arg_delete = cJSON_GetObjectItem(json, "delete");
|
||||
if (arg_delete == NULL || !cJSON_IsBool(arg_delete)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON *arg_relpath = cJSON_GetObjectItem(json, "relpath");
|
||||
if (arg_relpath == NULL || !cJSON_IsString(arg_relpath)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON *arg_doc_id = cJSON_GetObjectItem(json, "doc_id");
|
||||
if (arg_doc_id == NULL || !cJSON_IsString(arg_doc_id)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tag_req_t *req = malloc(sizeof(tag_req_t));
|
||||
req->delete = arg_delete->valueint;
|
||||
req->name = arg_name->valuestring;
|
||||
req->relpath = arg_relpath->valuestring;
|
||||
req->doc_id = arg_doc_id->valuestring;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
void tag(struct mg_connection *nc, struct http_message *hm, struct mg_str *path) {
|
||||
if (path->len != UUID_STR_LEN + 4) {
|
||||
LOG_DEBUGF("serve.c", "Invalid tag path: %.*s", (int) path->len, path->p)
|
||||
mg_http_send_error(nc, 404, NULL);
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
return;
|
||||
}
|
||||
|
||||
char arg_index[UUID_STR_LEN];
|
||||
memcpy(arg_index, hm->uri.p + 5, UUID_STR_LEN);
|
||||
*(arg_index + UUID_STR_LEN - 1) = '\0';
|
||||
|
||||
if (hm->body.len < 2 || hm->method.len != 4 || memcmp(&hm->method, "POST", 4) == 0) {
|
||||
LOG_DEBUG("serve.c", "Invalid tag request")
|
||||
mg_http_send_error(nc, 400, NULL);
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
return;
|
||||
}
|
||||
|
||||
store_t *store = get_tag_store(arg_index);
|
||||
if (store == NULL) {
|
||||
LOG_DEBUGF("serve.c", "Could not get tag store for index: %s", arg_index)
|
||||
mg_http_send_error(nc, 404, NULL);
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
return;
|
||||
}
|
||||
|
||||
char *body = malloc(hm->body.len + 1);
|
||||
memcpy(body, hm->body.p, hm->body.len);
|
||||
*(body + hm->body.len) = '\0';
|
||||
cJSON *json = cJSON_Parse(body);
|
||||
|
||||
tag_req_t *arg_req = parse_tag_request(json);
|
||||
if (arg_req == NULL) {
|
||||
LOG_DEBUGF("serve.c", "Could not parse tag request", arg_index)
|
||||
cJSON_Delete(json);
|
||||
free(body);
|
||||
mg_http_send_error(nc, 400, NULL);
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *arr = NULL;
|
||||
|
||||
size_t data_len = 0;
|
||||
const char *data = store_read(store, arg_req->relpath, strlen(arg_req->relpath), &data_len);
|
||||
if (data_len == 0) {
|
||||
arr = cJSON_CreateArray();
|
||||
} else {
|
||||
arr = cJSON_Parse(data);
|
||||
}
|
||||
|
||||
if (arg_req->delete) {
|
||||
|
||||
if (data_len > 0) {
|
||||
cJSON *element = NULL;
|
||||
int i = 0;
|
||||
cJSON_ArrayForEach(element, arr) {
|
||||
if (strcmp(element->valuestring, arg_req->name) == 0) {
|
||||
cJSON_DeleteItemFromArray(arr, i);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
char buf[8196];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"{"
|
||||
" \"script\" : {"
|
||||
" \"source\": \"if (ctx._source.tag.contains(params.tag)) { ctx._source.tag.remove(ctx._source.tag.indexOf(params.tag)) }\","
|
||||
" \"lang\": \"painless\","
|
||||
" \"params\" : {"
|
||||
" \"tag\" : \"%s\""
|
||||
" }"
|
||||
" }"
|
||||
"}", arg_req->name
|
||||
);
|
||||
|
||||
char url[4096];
|
||||
snprintf(url, sizeof(url), "%s/sist2/_update/%s", WebCtx.es_url, arg_req->doc_id);
|
||||
nc->user_data = web_post_async(url, buf);
|
||||
|
||||
} else {
|
||||
cJSON_AddItemToArray(arr, cJSON_CreateString(arg_req->name));
|
||||
|
||||
char buf[8196];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"{"
|
||||
" \"script\" : {"
|
||||
" \"source\": \"ctx._source.tag.add(params.tag)\","
|
||||
" \"lang\": \"painless\","
|
||||
" \"params\" : {"
|
||||
" \"tag\" : \"%s\""
|
||||
" }"
|
||||
" }"
|
||||
"}", arg_req->name
|
||||
);
|
||||
|
||||
char url[4096];
|
||||
snprintf(url, sizeof(url), "%s/sist2/_update/%s", WebCtx.es_url, arg_req->doc_id);
|
||||
nc->user_data = web_post_async(url, buf);
|
||||
}
|
||||
|
||||
char *json_str = cJSON_PrintUnformatted(arr);
|
||||
store_write(store, arg_req->relpath, strlen(arg_req->relpath), json_str, strlen(json_str) + 1);
|
||||
|
||||
free(arg_req);
|
||||
free(json_str);
|
||||
cJSON_Delete(json);
|
||||
cJSON_Delete(arr);
|
||||
free(body);
|
||||
}
|
||||
|
||||
static void ev_router(struct mg_connection *nc, int ev, void *p) {
|
||||
struct mg_str scheme;
|
||||
struct mg_str user_info;
|
||||
@ -479,6 +643,8 @@ static void ev_router(struct mg_connection *nc, int ev, void *p) {
|
||||
thumbnail(nc, hm, &path);
|
||||
} else if (has_prefix(&path, &((struct mg_str) MG_MK_STR("/s/")))) {
|
||||
stats_files(nc, hm, &path);
|
||||
} else if (has_prefix(&path, &((struct mg_str) MG_MK_STR("/tag/")))) {
|
||||
tag(nc, hm, &path);
|
||||
} else if (has_prefix(&path, &((struct mg_str) MG_MK_STR("/d/")))) {
|
||||
document_info(nc, hm, &path);
|
||||
} else {
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user