Commit 80ab36e1 authored by jan.koester's avatar jan.koester
Browse files

test

parent 690f13f3
Loading
Loading
Loading
Loading
+28 −13
Original line number Diff line number Diff line
@@ -159,7 +159,7 @@ bool webedit::Api::handleRequest(libhttppp::HttpRequest &curreq, const int tid,

    // Route: /api/documents
    if (path == "/api/documents") {
        handleListDocuments(curreq);
        handleListDocuments(curreq, sessionid);
        return true;
    }

@@ -173,14 +173,14 @@ bool webedit::Api::handleRequest(libhttppp::HttpRequest &curreq, const int tid,
    // Route: /api/document/delete/{id}
    if (path.find("/api/document/delete/") == 0) {
        std::string docId = path.substr(21);
        handleDeleteDocument(curreq, docId);
        handleDeleteDocument(curreq, sessionid, docId);
        return true;
    }

    // Route: /api/document/revisions/{docId}
    if (path.find("/api/document/revisions/") == 0) {
        std::string docId = path.substr(24);
        handleListRevisions(curreq, docId);
        handleListRevisions(curreq, sessionid, docId);
        return true;
    }

@@ -847,15 +847,17 @@ void webedit::Api::handleSaveDocument(libhttppp::HttpRequest &curreq,
    std::string xml = exportTreeXml(doc);
    std::string docId;

    // Get author from session
    std::string author;
    // Get author, uid and domain from session
    std::string author, uid, domain;
    _session.getData(sessionid, "username", author);
    _session.getData(sessionid, "uid", uid);
    _session.getData(sessionid, "domain", domain);

    if (doc.currentDocId.empty()) {
        docId = _db.saveDocument(doc.currentDocName, xml, author);
        docId = _db.saveDocument(uid, domain, doc.currentDocName, xml, author);
        doc.currentDocId = docId;
    } else {
        docId = _db.updateDocument(doc.currentDocId, doc.currentDocName, xml, author);
        docId = _db.updateDocument(uid, doc.currentDocId, doc.currentDocName, xml, author);
    }

    json_object *resp = json_object_new_object();
@@ -872,8 +874,11 @@ void webedit::Api::handleSaveDocument(libhttppp::HttpRequest &curreq,
void webedit::Api::handleLoadDocument(libhttppp::HttpRequest &curreq,
                                       const std::string &sessionid,
                                       const std::string &docId) {
    std::string uid;
    _session.getData(sessionid, "uid", uid);

    std::string name, xml;
    if (!_db.loadDocument(docId, name, xml)) {
    if (!_db.loadDocument(uid, docId, name, xml)) {
        sendJsonError(curreq, 404, "Document not found");
        return;
    }
@@ -918,8 +923,11 @@ void webedit::Api::handleLoadDocument(libhttppp::HttpRequest &curreq,

// --- List documents ---

void webedit::Api::handleListDocuments(libhttppp::HttpRequest &curreq) {
    std::string jsonStr = _db.listDocuments();
void webedit::Api::handleListDocuments(libhttppp::HttpRequest &curreq,
                                        const std::string &sessionid) {
    std::string domain;
    _session.getData(sessionid, "domain", domain);
    std::string jsonStr = _db.listDocuments(domain);
    json_object *arr = json_tokener_parse(jsonStr.c_str());
    json_object *resp = json_object_new_object();
    json_object_object_add(resp, "documents", arr);
@@ -930,8 +938,11 @@ void webedit::Api::handleListDocuments(libhttppp::HttpRequest &curreq) {
// --- Delete document ---

void webedit::Api::handleDeleteDocument(libhttppp::HttpRequest &curreq,
                                         const std::string &sessionid,
                                         const std::string &docId) {
    _db.deleteDocument(docId);
    std::string uid;
    _session.getData(sessionid, "uid", uid);
    _db.deleteDocument(uid, docId);
    json_object *resp = json_object_new_object();
    json_object_object_add(resp, "status", json_object_new_string("ok"));
    sendJson(curreq, resp);
@@ -941,6 +952,7 @@ void webedit::Api::handleDeleteDocument(libhttppp::HttpRequest &curreq,
// --- Revision handlers ---

void webedit::Api::handleListRevisions(libhttppp::HttpRequest &curreq,
                                        const std::string &sessionid,
                                        const std::string &docId) {
    std::string jsonStr = _db.listRevisions(docId);
    json_object *arr = json_tokener_parse(jsonStr.c_str());
@@ -1016,7 +1028,10 @@ void webedit::Api::handleRestoreRevision(libhttppp::HttpRequest &curreq,
    std::string docId = json_object_get_string(docIdObj);
    std::string revId = json_object_get_string(revIdObj);

    if (!_db.restoreRevision(docId, revId)) {
    std::string uid;
    _session.getData(sessionid, "uid", uid);

    if (!_db.restoreRevision(uid, docId, revId)) {
        sendJsonError(curreq, 404, "Revision not found");
        if (req) json_object_put(req);
        return;
@@ -1024,7 +1039,7 @@ void webedit::Api::handleRestoreRevision(libhttppp::HttpRequest &curreq,

    // Reload the restored version into session
    std::string name, xml;
    _db.loadDocument(docId, name, xml);
    _db.loadDocument(uid, docId, name, xml);

    auto &doc = getDocState(sessionid);
    std::lock_guard<std::mutex> lk(doc.mtx);
+5 −3
Original line number Diff line number Diff line
@@ -86,11 +86,13 @@ namespace webedit {
        void handleSaveDocument(libhttppp::HttpRequest &curreq, const std::string &sessionid);
        void handleLoadDocument(libhttppp::HttpRequest &curreq, const std::string &sessionid,
                                const std::string &docId);
        void handleListDocuments(libhttppp::HttpRequest &curreq);
        void handleDeleteDocument(libhttppp::HttpRequest &curreq, const std::string &docId);
        void handleListDocuments(libhttppp::HttpRequest &curreq, const std::string &sessionid);
        void handleDeleteDocument(libhttppp::HttpRequest &curreq, const std::string &sessionid,
                                  const std::string &docId);

        // Revision handlers
        void handleListRevisions(libhttppp::HttpRequest &curreq, const std::string &docId);
        void handleListRevisions(libhttppp::HttpRequest &curreq, const std::string &sessionid,
                                 const std::string &docId);
        void handleLoadRevision(libhttppp::HttpRequest &curreq, const std::string &sessionid,
                                const std::string &revisionId);
        void handleRestoreRevision(libhttppp::HttpRequest &curreq, const std::string &sessionid);
+50 −16
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ void webedit::Database::initTables() {

    sql << "CREATE TABLE IF NOT EXISTS documents("
        <<   "id " << _db->getUUIDType(sql) << ","
        <<   "owner_uid text NOT NULL,"
        <<   "domain text NOT NULL DEFAULT '',"
        <<   "name varchar(255) NOT NULL,"
        <<   "xml text NOT NULL,"
        <<   "created timestamp DEFAULT CURRENT_TIMESTAMP,"
@@ -167,17 +169,21 @@ void webedit::Database::setOption(const std::string &key, const std::string &val
    _db->exec(sql, res);
}

std::string webedit::Database::saveDocument(const std::string &name, const std::string &xml,
std::string webedit::Database::saveDocument(const std::string &ownerUid,
                                            const std::string &domain,
                                            const std::string &name, const std::string &xml,
                                            const std::string &author) {
    uuid::uuid docId;
    docId.generate();

    dbpp::SQL sql;
    dbpp::DBResult res;
    std::vector<char> escapedName, escapedXml;
    std::vector<char> escapedName, escapedXml, escapedUid, escapedDomain;

    sql << "INSERT INTO documents (id, name, xml) VALUES ('"
    sql << "INSERT INTO documents (id, owner_uid, domain, name, xml) VALUES ('"
        << docId.c_str() << "','"
        << dbpp::SQL::escaped(escapedUid, ownerUid.c_str()) << "','"
        << dbpp::SQL::escaped(escapedDomain, domain.c_str()) << "','"
        << dbpp::SQL::escaped(escapedName, name.c_str()) << "','"
        << dbpp::SQL::escaped(escapedXml, xml.c_str()) << "');";

@@ -204,19 +210,22 @@ std::string webedit::Database::saveDocument(const std::string &name, const std::
    return docId.c_str();
}

std::string webedit::Database::updateDocument(const std::string &id,
std::string webedit::Database::updateDocument(const std::string &ownerUid,
                                               const std::string &id,
                                               const std::string &name,
                                               const std::string &xml,
                                               const std::string &author) {
    dbpp::SQL sql;
    dbpp::DBResult res;
    std::vector<char> escapedName, escapedXml, escapedId;
    std::vector<char> escapedName, escapedXml, escapedId, escapedUid;

    sql << "UPDATE documents SET name='"
        << dbpp::SQL::escaped(escapedName, name.c_str()) << "', xml='"
        << dbpp::SQL::escaped(escapedXml, xml.c_str())
        << "', modified=CURRENT_TIMESTAMP WHERE id='"
        << dbpp::SQL::escaped(escapedId, id.c_str()) << "';";
        << dbpp::SQL::escaped(escapedId, id.c_str())
        << "' AND owner_uid='"
        << dbpp::SQL::escaped(escapedUid, ownerUid.c_str()) << "';";

    _db->exec(sql, res);

@@ -257,14 +266,17 @@ std::string webedit::Database::updateDocument(const std::string &id,
    return id;
}

bool webedit::Database::loadDocument(const std::string &id,
bool webedit::Database::loadDocument(const std::string &ownerUid,
                                      const std::string &id,
                                      std::string &name, std::string &xml) {
    dbpp::SQL sql;
    dbpp::DBResult res;
    std::vector<char> escapedId;
    std::vector<char> escapedId, escapedUid;

    sql << "SELECT name, xml FROM documents WHERE id='"
        << dbpp::SQL::escaped(escapedId, id.c_str()) << "';";
        << dbpp::SQL::escaped(escapedId, id.c_str())
        << "' AND owner_uid='"
        << dbpp::SQL::escaped(escapedUid, ownerUid.c_str()) << "';";

    int rows = _db->exec(sql, res);
    if (rows < 1)
@@ -275,10 +287,25 @@ bool webedit::Database::loadDocument(const std::string &id,
    return true;
}

bool webedit::Database::deleteDocument(const std::string &id) {
bool webedit::Database::deleteDocument(const std::string &ownerUid,
                                       const std::string &id) {
    dbpp::SQL sql;
    dbpp::DBResult res;
    std::vector<char> escapedId;
    std::vector<char> escapedId, escapedUid;

    // Verify ownership before deleting
    sql << "SELECT id FROM documents WHERE id='"
        << dbpp::SQL::escaped(escapedId, id.c_str())
        << "' AND owner_uid='"
        << dbpp::SQL::escaped(escapedUid, ownerUid.c_str()) << "';";

    int rows = _db->exec(sql, res);
    if (rows < 1)
        return false;

    sql.clear();
    res.clear();
    escapedId.clear();

    // Delete revisions first
    sql << "DELETE FROM document_revisions WHERE document_id='"
@@ -288,19 +315,25 @@ bool webedit::Database::deleteDocument(const std::string &id) {
    sql.clear();
    res.clear();
    escapedId.clear();
    escapedUid.clear();

    sql << "DELETE FROM documents WHERE id='"
        << dbpp::SQL::escaped(escapedId, id.c_str()) << "';";
        << dbpp::SQL::escaped(escapedId, id.c_str())
        << "' AND owner_uid='"
        << dbpp::SQL::escaped(escapedUid, ownerUid.c_str()) << "';";

    _db->exec(sql, res);
    return true;
}

std::string webedit::Database::listDocuments() {
std::string webedit::Database::listDocuments(const std::string &domain) {
    dbpp::SQL sql;
    dbpp::DBResult res;
    std::vector<char> escapedDomain;

    sql << "SELECT id, name, created, modified FROM documents ORDER BY modified DESC;";
    sql << "SELECT id, name, created, modified FROM documents WHERE domain='"
        << dbpp::SQL::escaped(escapedDomain, domain.c_str())
        << "' ORDER BY modified DESC;";

    int rows = _db->exec(sql, res);

@@ -366,13 +399,14 @@ bool webedit::Database::loadRevision(const std::string &revisionId,
    return true;
}

bool webedit::Database::restoreRevision(const std::string &docId,
bool webedit::Database::restoreRevision(const std::string &ownerUid,
                                         const std::string &docId,
                                         const std::string &revisionId) {
    std::string name, xml;
    if (!loadRevision(revisionId, name, xml))
        return false;

    updateDocument(docId, name, xml);
    updateDocument(ownerUid, docId, name, xml);
    return true;
}

+12 −8
Original line number Diff line number Diff line
@@ -40,19 +40,23 @@ namespace webedit {

        void initTables();

        // Document CRUD
        std::string saveDocument(const std::string &name, const std::string &xml,
        // Document CRUD (per-user + per-domain)
        std::string saveDocument(const std::string &ownerUid, const std::string &domain,
                                 const std::string &name, const std::string &xml,
                                 const std::string &author = "");
        std::string updateDocument(const std::string &id, const std::string &name,
                                   const std::string &xml, const std::string &author = "");
        bool loadDocument(const std::string &id, std::string &name, std::string &xml);
        bool deleteDocument(const std::string &id);
        std::string listDocuments(); // Returns JSON array
        std::string updateDocument(const std::string &ownerUid, const std::string &id,
                                   const std::string &name, const std::string &xml,
                                   const std::string &author = "");
        bool loadDocument(const std::string &ownerUid, const std::string &id,
                          std::string &name, std::string &xml);
        bool deleteDocument(const std::string &ownerUid, const std::string &id);
        std::string listDocuments(const std::string &domain); // Returns JSON array

        // Revision support
        bool loadRevision(const std::string &revisionId, std::string &name, std::string &xml);
        std::string listRevisions(const std::string &docId); // Returns JSON array
        bool restoreRevision(const std::string &docId, const std::string &revisionId);
        bool restoreRevision(const std::string &ownerUid, const std::string &docId,
                             const std::string &revisionId);

        // Options (key-value config store)
        std::string getOption(const std::string &key, const std::string &defaultVal = "");
+1 −0
Original line number Diff line number Diff line
@@ -357,6 +357,7 @@ void webedit::Server::handleLogin(libhttppp::HttpRequest &curreq,

        _session.addData(sessionid, "uid", uidStr);
        _session.addData(sessionid, "username", displayname);
        _session.addData(sessionid, "domain", domain);

        // Set authid cookie and return success
        libhttppp::HttpResponse resp;