Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,16 @@ jobs:
submodules: recursive
fetch-depth: 0

- name: Install system dependencies
- name: Switch apt mirror
run: |
# GitHub 托管 runner 默认走 Azure 镜像,下载系统库容易卡住,切换到官方源
if [ -f /etc/apt/apt-mirrors.txt ]; then
sudo sed -i 's/azure\.archive\.ubuntu\.com/archive.ubuntu.com/g' /etc/apt/apt-mirrors.txt
fi
sudo apt-get update

- name: Install system dependencies
run: |
sudo apt-get install -y \
build-essential \
ninja-build \
Expand Down
3 changes: 2 additions & 1 deletion dbproxy/xml/category.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
<column name="is_deleted" type="int32"/>
<column name="create_time" type="datetime" default="current_timestamp"/>
<column name="update_time" type="datetime" update="current_timestamp"/>
<column name="user_id" type="int64"/>
</columns>
<indexs>
<index name="pk" cols="id" type="pk"/>
<index name="name" cols="name" type="uniq"/>
<index name="user_id_name" cols="user_id,name" type="uniq"/>
</indexs>
</table>
12 changes: 8 additions & 4 deletions src/blog_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -431,10 +431,14 @@ void BlogModule::registerServlets()
dp->addServlet("/api/v1/admin/database/exports/:id/delete", XX(ExportDeleteServlet));
dp->addServlet("/api/v1/admin/database/exports/:id/download", XX(ExportDownloadServlet));
dp->addServlet("/api/v1/admin/database/cleanup", XX(CleanupServlet));
// 文章分类相关
dp->addServlet("/api/v1/category/admin/create", XX(CategoryCreateServlet));
dp->addServlet("/api/v1/category/admin/delete", XX(CategoryDeleteServlet));
dp->addServlet("/api/v1/category/admin/query", XX(CategoryQueryServlet));
// 文章分类相关(个人分类,登录后使用)
dp->addServlet("/api/v1/category/create", XX(CategoryCreateServlet));
dp->addServlet("/api/v1/category/delete", XX(CategoryDeleteServlet));
dp->addServlet("/api/v1/category/query", XX(CategoryQueryServlet));
// 管理端分类管理(可跨用户操作)
dp->addServlet("/api/v1/category/admin/create", XX(CategoryAdminCreateServlet));
dp->addServlet("/api/v1/category/admin/delete", XX(CategoryAdminDeleteServlet));
dp->addServlet("/api/v1/category/admin/query", XX(CategoryAdminQueryServlet));
// 错误码下发(公开接口)
dp->addServlet("/api/v1/error-codes", XX(ErrorCodesServlet));
// 站点公开配置
Expand Down
10 changes: 8 additions & 2 deletions src/index.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ static chen::ConfigVar<std::string>::ptr g_jieba_dict_path =

static chen::ConfigVar<std::string>::ptr g_index_path = chen::Config::Lookup("search.index_path", std::string(""), "search index file path");

/// 索引文件格式版本。数据结构/内容口径变化时递增,旧文件会被丢弃并全量重建
static const uint32_t kIndexFileVersion = 2;

Index::Index()
: m_createTime(0)
, m_endTime(0)
Expand Down Expand Up @@ -358,7 +361,7 @@ bool Index::save(const std::string& path)
}
chen::ByteArray::ptr ba(new chen::ByteArray);
ba->writeUint32(0x42494458); // magic 'BIDX'
ba->writeUint32(1); // version
ba->writeUint32(kIndexFileVersion);
ba->writeInt64(m_docs.size());
for (auto id : m_docs) {
ba->writeInt64(id);
Expand Down Expand Up @@ -419,7 +422,10 @@ bool Index::load(const std::string& path)
return false;
}
uint32_t version = ba->readUint32();
(void)version;
if (version != kIndexFileVersion) {
WARN(logger) << "index file version mismatch, file=" << version << " expect=" << kIndexFileVersion << ", rebuild from scratch";
return false;
}

initJieba();

Expand Down
36 changes: 30 additions & 6 deletions src/manager/category_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace blog {
static chen::Logger::ptr logger = LOG_ROOT();

static const int kCacheTTL = 300;
static const char* kCachePrefix = "cache:obj:cat:";
static const char* kCachePrefix = "cache:obj:cat2:";

static std::string categoryCacheKey(int64_t id)
{
Expand All @@ -22,6 +22,7 @@ static data::CategoryInfo::ptr parseCategoryCache(const Json::Value& v)
{
auto info = std::make_shared<data::CategoryInfo>();
info->setId(cacheJsonInt64(v, "id"));
info->setUserId(cacheJsonInt64(v, "user_id"));
info->setName(cacheJsonString(v, "name"));
info->setColor(cacheJsonString(v, "color"));
info->setDescription(cacheJsonString(v, "description"));
Expand Down Expand Up @@ -104,21 +105,44 @@ void CategoryManager::listAll(std::vector<blog::data::CategoryInfo::ptr>& infos,
}
}

blog::data::CategoryInfo::ptr CategoryManager::getByName(const std::string& name)
bool CategoryManager::listByUserId(std::vector<blog::data::CategoryInfo::ptr>& infos, int64_t user_id, bool isValid)
{
int64_t cachedId = getCachedIdMapping("cat:name:" + name);
auto db = GetDB();
if (!db) {
ERROR(logger) << "Get DB connection fail";
return false;
}
auto qb = data::CategoryInfoDao::newQuery();
qb->where("user_id", "=", user_id);
qb->whereIf(isValid, "is_deleted", "=", (int64_t)0);
qb->orderBy("id", "ASC");
if (data::CategoryInfoDao::QueryByBuilder(infos, qb, db)) {
ERROR(logger) << "QueryByBuilder failed";
return false;
}
return true;
}

blog::data::CategoryInfo::ptr CategoryManager::getByUserIdName(int64_t user_id, const std::string& name)
{
std::string cache_key = "cat:uid_name:" + std::to_string(user_id) + ":" + name;
int64_t cachedId = getCachedIdMapping(cache_key);
if (cachedId > 0) {
return get(cachedId);
auto cached = get(cachedId);
// 改名后旧映射可能仍在缓存中,校验归属和名称,避免命中脏数据
if (cached && cached->getUserId() == user_id && cached->getName() == name) {
return cached;
}
}
auto db = GetDB();
if (!db) {
ERROR(logger) << "Get DB connection fail";
return nullptr;
}
auto info = data::CategoryInfoDao::QueryByName(name, db);
auto info = data::CategoryInfoDao::QueryByUserIdName(user_id, name, db);
if (info) {
setCategoryCache(info);
cacheIdMapping("cat:name:" + name, info->getId());
cacheIdMapping(cache_key, info->getId());
}
return info;
}
Expand Down
4 changes: 3 additions & 1 deletion src/manager/category_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ class CategoryManager {
public:
void listAll(std::vector<blog::data::CategoryInfo::ptr>& infos, bool isValid = false);

bool listByUserId(std::vector<blog::data::CategoryInfo::ptr>& infos, int64_t user_id, bool isValid = false);

void add(blog::data::CategoryInfo::ptr info);

blog::data::CategoryInfo::ptr get(int64_t id);

blog::data::CategoryInfo::ptr getByName(const std::string& name);
blog::data::CategoryInfo::ptr getByUserIdName(int64_t user_id, const std::string& name);

private:
static data::CategoryInfo::ptr parseRow(chen::ISQLData::ptr rt);
Expand Down
34 changes: 34 additions & 0 deletions src/permission.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#pragma once

#include "error_codes.h"
#include "manager/organization_manager.h"
#include "manager/user_manager.h"

Expand Down Expand Up @@ -182,5 +183,38 @@ inline bool CanBypassReview(int32_t system_role)
return system_role == UserManager::Role::ADMIN || system_role == UserManager::Role::CHECKER;
}

// ============================================================================
// 管理端路由权限
// ============================================================================

/// 是否系统管理员
inline bool IsAdmin(int32_t system_role)
{
return system_role == UserManager::Role::ADMIN;
}

/**
* @brief 校验管理端路由访问权限
* @param uid 当前登录用户 id
* @param user 当前登录用户信息
* @return 0 表示通过,否则返回需要下发的错误码
*/
inline int32_t CheckAdminRoute(int64_t uid, const data::UserInfo::ptr& user)
{
if (!uid) {
return errcode::NOT_LOGIN;
}
if (!user) {
return errcode::USER_NOT_FOUND;
}
if (user->getState() != UserManager::Status::ACTIVE || user->getIsDeleted()) {
return errcode::ACCOUNT_INVALID;
}
if (!IsAdmin(user->getRole())) {
return errcode::ACCESS_DENIED;
}
return 0;
}

} // namespace permission
} // namespace blog
3 changes: 2 additions & 1 deletion src/servlets/article/article_create_servlet.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ int32_t ArticleCreateServlet::handle(
for (auto& s : cat_ids) {
int64_t cid = chen::TypeUtil::Atoi(s);
auto cinfo = CategoryMgr::GetInstance()->get(cid);
if (!cinfo) {
if (!cinfo || cinfo->getUserId() != uid) {
WARN(logger) << "skip category not owned by user, user_id=" << uid << " category_id=" << cid;
continue;
}
data::ArticleCategoryRelInfo::ptr rel(new data::ArticleCategoryRelInfo);
Expand Down
10 changes: 8 additions & 2 deletions src/servlets/article/article_update_category_servlet.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <chen/log/log.h>

#include "../../index.h"
#include "../../manager/article_category_rel_manager.h"
#include "../../manager/article_manager.h"
#include "../../manager/category_manager.h"
Expand Down Expand Up @@ -73,7 +74,8 @@ int32_t ArticleUpdateCategoryServlet::handle(
for (auto& i : tmps) {
int64_t cid = chen::TypeUtil::Atoi(i);
auto cinfo = CategoryMgr::GetInstance()->get(cid);
if (!cinfo) {
if (!cinfo || cinfo->getUserId() != uid) {
WARN(logger) << "skip category not owned by user, user_id=" << uid << " category_id=" << cid;
continue;
}
auto acinfo = ArticleCategoryRelMgr::GetInstance()->getByArticleIdCategoryId(id, cid);
Expand Down Expand Up @@ -104,7 +106,8 @@ int32_t ArticleUpdateCategoryServlet::handle(
for (auto& i : tmps) {
int64_t cid = chen::TypeUtil::Atoi(i);
auto cinfo = CategoryMgr::GetInstance()->get(cid);
if (!cinfo) {
if (!cinfo || cinfo->getUserId() != uid) {
WARN(logger) << "skip category not owned by user, user_id=" << uid << " category_id=" << cid;
continue;
}
auto acinfo = ArticleCategoryRelMgr::GetInstance()->getByArticleIdCategoryId(id, cid);
Expand Down Expand Up @@ -132,6 +135,9 @@ int32_t ArticleUpdateCategoryServlet::handle(
break;
}

// 分类关联已变化,同步刷新搜索索引中的 CAT_ID,避免按分类筛选结果滞后
IndexMgr::GetInstance()->updateArticle(ainfo);

if (!update_add_infos.empty()) {
auto& v = result->jsondata["add_category_ids"];
for (auto& i : update_add_infos) {
Expand Down
7 changes: 6 additions & 1 deletion src/servlets/article/article_update_servlet.cc
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,17 @@ int32_t ArticleUpdateServlet::syncCategoryRels(chen::http::HttpRequest::ptr requ
std::vector<data::ArticleCategoryRelInfo::ptr> old_rels;
ArticleCategoryRelMgr::GetInstance()->listByArticleId(old_rels, id, true);

int64_t uid = getUserId(request);
if (!uid) {
return 0;
}

std::set<int64_t> new_cat_ids;
auto cat_id_strs = chen::StringUtil::Split(category, ',');
for (auto& s : cat_id_strs) {
int64_t cid = chen::TypeUtil::Atoi(s);
auto cinfo = CategoryMgr::GetInstance()->get(cid);
if (cinfo) {
if (cinfo && cinfo->getUserId() == uid) {
new_cat_ids.insert(cid);
}
}
Expand Down
Loading
Loading