1
0
mirror of https://github.com/haiwen/seafile-server.git synced 2025-06-29 16:36:52 +00:00
seafile-server/common/branch-mgr.c
feiniks d6f6127641
Add notification server (#535)
* Add notification server of go

Modify path of pkg

Send notification for update-repo event

Delete client pkg and use reflect select to send message

Modify output of log

Add formatter of log

Add jwt authentication

go add get jwt token api

CI support compile libjwt

Get group users from database

* Add ping to test mysql is alive

Co-authored-by: 杨赫然 <heran.yang@seafile.com>
2022-12-16 15:29:01 +08:00

669 lines
18 KiB
C

#include "common.h"
#include "log.h"
#ifndef SEAFILE_SERVER
#include "db.h"
#else
#include "seaf-db.h"
#endif
#include "seafile-session.h"
#ifdef FULL_FEATURE
#include "notif-mgr.h"
#endif
#include "branch-mgr.h"
#define BRANCH_DB "branch.db"
SeafBranch *
seaf_branch_new (const char *name, const char *repo_id, const char *commit_id)
{
SeafBranch *branch;
branch = g_new0 (SeafBranch, 1);
branch->name = g_strdup (name);
memcpy (branch->repo_id, repo_id, 36);
branch->repo_id[36] = '\0';
memcpy (branch->commit_id, commit_id, 40);
branch->commit_id[40] = '\0';
branch->ref = 1;
return branch;
}
void
seaf_branch_free (SeafBranch *branch)
{
if (branch == NULL) return;
g_free (branch->name);
g_free (branch);
}
void
seaf_branch_list_free (GList *blist)
{
GList *ptr;
for (ptr = blist; ptr; ptr = ptr->next) {
seaf_branch_unref (ptr->data);
}
g_list_free (blist);
}
void
seaf_branch_set_commit (SeafBranch *branch, const char *commit_id)
{
memcpy (branch->commit_id, commit_id, 40);
branch->commit_id[40] = '\0';
}
void
seaf_branch_ref (SeafBranch *branch)
{
branch->ref++;
}
void
seaf_branch_unref (SeafBranch *branch)
{
if (!branch)
return;
if (--branch->ref <= 0)
seaf_branch_free (branch);
}
struct _SeafBranchManagerPriv {
sqlite3 *db;
#ifndef SEAFILE_SERVER
pthread_mutex_t db_lock;
#endif
};
static int open_db (SeafBranchManager *mgr);
SeafBranchManager *
seaf_branch_manager_new (struct _SeafileSession *seaf)
{
SeafBranchManager *mgr;
mgr = g_new0 (SeafBranchManager, 1);
mgr->priv = g_new0 (SeafBranchManagerPriv, 1);
mgr->seaf = seaf;
#ifndef SEAFILE_SERVER
pthread_mutex_init (&mgr->priv->db_lock, NULL);
#endif
return mgr;
}
int
seaf_branch_manager_init (SeafBranchManager *mgr)
{
return open_db (mgr);
}
static int
open_db (SeafBranchManager *mgr)
{
if (!mgr->seaf->create_tables && seaf_db_type (mgr->seaf->db) != SEAF_DB_TYPE_PGSQL)
return 0;
#ifndef SEAFILE_SERVER
char *db_path;
const char *sql;
db_path = g_build_filename (mgr->seaf->seaf_dir, BRANCH_DB, NULL);
if (sqlite_open_db (db_path, &mgr->priv->db) < 0) {
g_critical ("[Branch mgr] Failed to open branch db\n");
g_free (db_path);
return -1;
}
g_free (db_path);
sql = "CREATE TABLE IF NOT EXISTS Branch ("
"name TEXT, repo_id TEXT, commit_id TEXT);";
if (sqlite_query_exec (mgr->priv->db, sql) < 0)
return -1;
sql = "CREATE INDEX IF NOT EXISTS branch_index ON Branch(repo_id, name);";
if (sqlite_query_exec (mgr->priv->db, sql) < 0)
return -1;
#elif defined FULL_FEATURE
char *sql;
switch (seaf_db_type (mgr->seaf->db)) {
case SEAF_DB_TYPE_MYSQL:
sql = "CREATE TABLE IF NOT EXISTS Branch ("
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
"name VARCHAR(10), repo_id CHAR(41), commit_id CHAR(41),"
"UNIQUE INDEX(repo_id, name)) ENGINE = INNODB";
if (seaf_db_query (mgr->seaf->db, sql) < 0)
return -1;
break;
case SEAF_DB_TYPE_PGSQL:
sql = "CREATE TABLE IF NOT EXISTS Branch ("
"name VARCHAR(10), repo_id CHAR(40), commit_id CHAR(40),"
"PRIMARY KEY (repo_id, name))";
if (seaf_db_query (mgr->seaf->db, sql) < 0)
return -1;
break;
case SEAF_DB_TYPE_SQLITE:
sql = "CREATE TABLE IF NOT EXISTS Branch ("
"name VARCHAR(10), repo_id CHAR(41), commit_id CHAR(41),"
"PRIMARY KEY (repo_id, name))";
if (seaf_db_query (mgr->seaf->db, sql) < 0)
return -1;
break;
}
#endif
return 0;
}
int
seaf_branch_manager_add_branch (SeafBranchManager *mgr, SeafBranch *branch)
{
#ifndef SEAFILE_SERVER
char sql[256];
pthread_mutex_lock (&mgr->priv->db_lock);
sqlite3_snprintf (sizeof(sql), sql,
"SELECT 1 FROM Branch WHERE name=%Q and repo_id=%Q",
branch->name, branch->repo_id);
if (sqlite_check_for_existence (mgr->priv->db, sql))
sqlite3_snprintf (sizeof(sql), sql,
"UPDATE Branch SET commit_id=%Q WHERE "
"name=%Q and repo_id=%Q",
branch->commit_id, branch->name, branch->repo_id);
else
sqlite3_snprintf (sizeof(sql), sql,
"INSERT INTO Branch (name, repo_id, commit_id) VALUES (%Q, %Q, %Q)",
branch->name, branch->repo_id, branch->commit_id);
sqlite_query_exec (mgr->priv->db, sql);
pthread_mutex_unlock (&mgr->priv->db_lock);
return 0;
#else
char *sql;
SeafDB *db = mgr->seaf->db;
if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) {
gboolean exists, err;
int rc;
sql = "SELECT repo_id FROM Branch WHERE name=? AND repo_id=?";
exists = seaf_db_statement_exists(db, sql, &err,
2, "string", branch->name,
"string", branch->repo_id);
if (err)
return -1;
if (exists)
rc = seaf_db_statement_query (db,
"UPDATE Branch SET commit_id=? "
"WHERE name=? AND repo_id=?",
3, "string", branch->commit_id,
"string", branch->name,
"string", branch->repo_id);
else
rc = seaf_db_statement_query (db,
"INSERT INTO Branch (name, repo_id, commit_id) VALUES (?, ?, ?)",
3, "string", branch->name,
"string", branch->repo_id,
"string", branch->commit_id);
if (rc < 0)
return -1;
} else {
int rc = seaf_db_statement_query (db,
"REPLACE INTO Branch (name, repo_id, commit_id) VALUES (?, ?, ?)",
3, "string", branch->name,
"string", branch->repo_id,
"string", branch->commit_id);
if (rc < 0)
return -1;
}
return 0;
#endif
}
int
seaf_branch_manager_del_branch (SeafBranchManager *mgr,
const char *repo_id,
const char *name)
{
#ifndef SEAFILE_SERVER
char *sql;
pthread_mutex_lock (&mgr->priv->db_lock);
sql = sqlite3_mprintf ("DELETE FROM Branch WHERE name = %Q AND "
"repo_id = '%s'", name, repo_id);
if (sqlite_query_exec (mgr->priv->db, sql) < 0)
seaf_warning ("Delete branch %s failed\n", name);
sqlite3_free (sql);
pthread_mutex_unlock (&mgr->priv->db_lock);
return 0;
#else
int rc = seaf_db_statement_query (mgr->seaf->db,
"DELETE FROM Branch WHERE name=? AND repo_id=?",
2, "string", name, "string", repo_id);
if (rc < 0)
return -1;
return 0;
#endif
}
int
seaf_branch_manager_update_branch (SeafBranchManager *mgr, SeafBranch *branch)
{
#ifndef SEAFILE_SERVER
sqlite3 *db;
char *sql;
pthread_mutex_lock (&mgr->priv->db_lock);
db = mgr->priv->db;
sql = sqlite3_mprintf ("UPDATE Branch SET commit_id = %Q "
"WHERE name = %Q AND repo_id = %Q",
branch->commit_id, branch->name, branch->repo_id);
sqlite_query_exec (db, sql);
sqlite3_free (sql);
pthread_mutex_unlock (&mgr->priv->db_lock);
return 0;
#else
int rc = seaf_db_statement_query (mgr->seaf->db,
"UPDATE Branch SET commit_id = ? "
"WHERE name = ? AND repo_id = ?",
3, "string", branch->commit_id,
"string", branch->name,
"string", branch->repo_id);
if (rc < 0)
return -1;
return 0;
#endif
}
#if defined( SEAFILE_SERVER ) && defined( FULL_FEATURE )
#include "mq-mgr.h"
static gboolean
get_commit_id (SeafDBRow *row, void *data)
{
char *out_commit_id = data;
const char *commit_id;
commit_id = seaf_db_row_get_column_text (row, 0);
memcpy (out_commit_id, commit_id, 41);
out_commit_id[40] = '\0';
return FALSE;
}
static void
publish_repo_update_event (const char *repo_id, const char *commit_id)
{
char buf[128];
snprintf (buf, sizeof(buf), "repo-update\t%s\t%s",
repo_id, commit_id);
seaf_mq_manager_publish_event (seaf->mq_mgr, SEAFILE_SERVER_CHANNEL_EVENT, buf);
}
static void
notify_repo_update (const char *repo_id, const char *commit_id)
{
json_t *event = NULL;
json_t *content = NULL;
char *msg = NULL;
event = json_object ();
content = json_object ();
json_object_set_new (event, "type", json_string("repo-update"));
json_object_set_new (content, "repo_id", json_string(repo_id));
json_object_set_new (content, "commit_id", json_string(commit_id));
json_object_set_new (event, "content", content);
msg = json_dumps (event, JSON_COMPACT);
if (seaf->notif_mgr)
seaf_notif_manager_send_event (seaf->notif_mgr, msg);
json_decref (event);
g_free (msg);
}
static void
on_branch_updated (SeafBranchManager *mgr, SeafBranch *branch)
{
seaf_repo_manager_update_repo_info (seaf->repo_mgr, branch->repo_id, branch->commit_id);
notify_repo_update(branch->repo_id, branch->commit_id);
if (seaf_repo_manager_is_virtual_repo (seaf->repo_mgr, branch->repo_id))
return;
publish_repo_update_event (branch->repo_id, branch->commit_id);
}
int
seaf_branch_manager_test_and_update_branch (SeafBranchManager *mgr,
SeafBranch *branch,
const char *old_commit_id)
{
SeafDBTrans *trans;
char *sql;
char commit_id[41] = { 0 };
trans = seaf_db_begin_transaction (mgr->seaf->db);
if (!trans)
return -1;
switch (seaf_db_type (mgr->seaf->db)) {
case SEAF_DB_TYPE_MYSQL:
case SEAF_DB_TYPE_PGSQL:
sql = "SELECT commit_id FROM Branch WHERE name=? "
"AND repo_id=? FOR UPDATE";
break;
case SEAF_DB_TYPE_SQLITE:
sql = "SELECT commit_id FROM Branch WHERE name=? "
"AND repo_id=?";
break;
default:
g_return_val_if_reached (-1);
}
if (seaf_db_trans_foreach_selected_row (trans, sql,
get_commit_id, commit_id,
2, "string", branch->name,
"string", branch->repo_id) < 0) {
seaf_db_rollback (trans);
seaf_db_trans_close (trans);
return -1;
}
if (strcmp (old_commit_id, commit_id) != 0) {
seaf_db_rollback (trans);
seaf_db_trans_close (trans);
return -1;
}
sql = "UPDATE Branch SET commit_id = ? "
"WHERE name = ? AND repo_id = ?";
if (seaf_db_trans_query (trans, sql, 3, "string", branch->commit_id,
"string", branch->name,
"string", branch->repo_id) < 0) {
seaf_db_rollback (trans);
seaf_db_trans_close (trans);
return -1;
}
if (seaf_db_commit (trans) < 0) {
seaf_db_rollback (trans);
seaf_db_trans_close (trans);
return -1;
}
seaf_db_trans_close (trans);
on_branch_updated (mgr, branch);
return 0;
}
#endif
#ifndef SEAFILE_SERVER
static SeafBranch *
real_get_branch (SeafBranchManager *mgr,
const char *repo_id,
const char *name)
{
SeafBranch *branch = NULL;
sqlite3_stmt *stmt;
sqlite3 *db;
char *sql;
int result;
pthread_mutex_lock (&mgr->priv->db_lock);
db = mgr->priv->db;
sql = sqlite3_mprintf ("SELECT commit_id FROM Branch "
"WHERE name = %Q and repo_id='%s'",
name, repo_id);
if (!(stmt = sqlite_query_prepare (db, sql))) {
seaf_warning ("[Branch mgr] Couldn't prepare query %s\n", sql);
sqlite3_free (sql);
pthread_mutex_unlock (&mgr->priv->db_lock);
return NULL;
}
sqlite3_free (sql);
result = sqlite3_step (stmt);
if (result == SQLITE_ROW) {
char *commit_id = (char *)sqlite3_column_text (stmt, 0);
branch = seaf_branch_new (name, repo_id, commit_id);
pthread_mutex_unlock (&mgr->priv->db_lock);
sqlite3_finalize (stmt);
return branch;
} else if (result == SQLITE_ERROR) {
const char *str = sqlite3_errmsg (db);
seaf_warning ("Couldn't prepare query, error: %d->'%s'\n",
result, str ? str : "no error given");
}
sqlite3_finalize (stmt);
pthread_mutex_unlock (&mgr->priv->db_lock);
return NULL;
}
SeafBranch *
seaf_branch_manager_get_branch (SeafBranchManager *mgr,
const char *repo_id,
const char *name)
{
SeafBranch *branch;
/* "fetch_head" maps to "local" or "master" on client (LAN sync) */
if (strcmp (name, "fetch_head") == 0) {
branch = real_get_branch (mgr, repo_id, "local");
if (!branch) {
branch = real_get_branch (mgr, repo_id, "master");
}
return branch;
} else {
return real_get_branch (mgr, repo_id, name);
}
}
#else
static gboolean
get_branch (SeafDBRow *row, void *vid)
{
char *ret = vid;
const char *commit_id;
commit_id = seaf_db_row_get_column_text (row, 0);
memcpy (ret, commit_id, 41);
return FALSE;
}
static SeafBranch *
real_get_branch (SeafBranchManager *mgr,
const char *repo_id,
const char *name)
{
char commit_id[41];
char *sql;
commit_id[0] = 0;
sql = "SELECT commit_id FROM Branch WHERE name=? AND repo_id=?";
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
get_branch, commit_id,
2, "string", name, "string", repo_id) < 0) {
seaf_warning ("[branch mgr] DB error when get branch %s.\n", name);
return NULL;
}
if (commit_id[0] == 0)
return NULL;
return seaf_branch_new (name, repo_id, commit_id);
}
SeafBranch *
seaf_branch_manager_get_branch (SeafBranchManager *mgr,
const char *repo_id,
const char *name)
{
SeafBranch *branch;
/* "fetch_head" maps to "master" on server. */
if (strcmp (name, "fetch_head") == 0) {
branch = real_get_branch (mgr, repo_id, "master");
return branch;
} else {
return real_get_branch (mgr, repo_id, name);
}
}
#endif /* not SEAFILE_SERVER */
gboolean
seaf_branch_manager_branch_exists (SeafBranchManager *mgr,
const char *repo_id,
const char *name)
{
#ifndef SEAFILE_SERVER
char *sql;
gboolean ret;
pthread_mutex_lock (&mgr->priv->db_lock);
sql = sqlite3_mprintf ("SELECT name FROM Branch WHERE name = %Q "
"AND repo_id='%s'", name, repo_id);
ret = sqlite_check_for_existence (mgr->priv->db, sql);
sqlite3_free (sql);
pthread_mutex_unlock (&mgr->priv->db_lock);
return ret;
#else
gboolean db_err = FALSE;
return seaf_db_statement_exists (mgr->seaf->db,
"SELECT name FROM Branch WHERE name=? "
"AND repo_id=?", &db_err,
2, "string", name, "string", repo_id);
#endif
}
#ifndef SEAFILE_SERVER
GList *
seaf_branch_manager_get_branch_list (SeafBranchManager *mgr,
const char *repo_id)
{
sqlite3 *db = mgr->priv->db;
int result;
sqlite3_stmt *stmt;
char sql[256];
char *name;
char *commit_id;
GList *ret = NULL;
SeafBranch *branch;
snprintf (sql, 256, "SELECT name, commit_id FROM branch WHERE repo_id ='%s'",
repo_id);
pthread_mutex_lock (&mgr->priv->db_lock);
if ( !(stmt = sqlite_query_prepare(db, sql)) ) {
pthread_mutex_unlock (&mgr->priv->db_lock);
return NULL;
}
while (1) {
result = sqlite3_step (stmt);
if (result == SQLITE_ROW) {
name = (char *)sqlite3_column_text(stmt, 0);
commit_id = (char *)sqlite3_column_text(stmt, 1);
branch = seaf_branch_new (name, repo_id, commit_id);
ret = g_list_prepend (ret, branch);
}
if (result == SQLITE_DONE)
break;
if (result == SQLITE_ERROR) {
const gchar *str = sqlite3_errmsg (db);
seaf_warning ("Couldn't prepare query, error: %d->'%s'\n",
result, str ? str : "no error given");
sqlite3_finalize (stmt);
seaf_branch_list_free (ret);
pthread_mutex_unlock (&mgr->priv->db_lock);
return NULL;
}
}
sqlite3_finalize (stmt);
pthread_mutex_unlock (&mgr->priv->db_lock);
return g_list_reverse(ret);
}
#else
static gboolean
get_branches (SeafDBRow *row, void *vplist)
{
GList **plist = vplist;
const char *commit_id;
const char *name;
const char *repo_id;
SeafBranch *branch;
name = seaf_db_row_get_column_text (row, 0);
repo_id = seaf_db_row_get_column_text (row, 1);
commit_id = seaf_db_row_get_column_text (row, 2);
branch = seaf_branch_new (name, repo_id, commit_id);
*plist = g_list_prepend (*plist, branch);
return TRUE;
}
GList *
seaf_branch_manager_get_branch_list (SeafBranchManager *mgr,
const char *repo_id)
{
GList *ret = NULL;
char *sql;
sql = "SELECT name, repo_id, commit_id FROM Branch WHERE repo_id=?";
if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
get_branches, &ret,
1, "string", repo_id) < 0) {
seaf_warning ("[branch mgr] DB error when get branch list.\n");
return NULL;
}
return ret;
}
#endif