【网盘项目日志】20210526:Seafile 搜索系统开发日志(2)

默默呆,继续做搜索系统啊

文章目录

    • 研究一下已经给出的前端文件
    • seafevents 的难题
    • 分析缺失的接口
    • 分析 Pro 包中可以使用的代码
    • 开始运行已有代码

研究一下已经给出的前端文件

打开 frontend/src/components/search/search.js,找到了一些代码片段:

...
  getSearchResult(queryData) {

    ......

    this.source = seafileAPI.getSource();
    ......
  }

......
  sendRequest(queryData, cancelToken) {
    var _this = this;
    let isPublic = this.props.isPublic;

    if (isPublic) {
      seafileAPI.searchFilesInPublishedRepo(queryData.search_repo, queryData.q).then(res => {
        ......
    } else {
      seafileAPI.searchFiles(queryData,cancelToken).then(res => {
        ......
    }
  }
......

使用 IDEA 的查找,找到 SeafileAPI 的定义类,发现他是使用了一个 node module,当然是他自己提供的,名字叫 seafile-js。

精确查找一下刚才找到的 getSource, searchFilesInPublishedRepo, searchFiles 这几个函数,可以找到:

......
  }, {
    key: 'getSource',
    value: function getSource() {
      // for search
      var CancelToken = axios.CancelToken;
      var source = CancelToken.source();
      return source;
    }
  }, {
    key: 'searchFilesInPublishedRepo',
    value: function searchFilesInPublishedRepo(repoID, q, page, perPage) {
      var url = this.server + '/api/v2.1/published-repo-search/';
      ......
    }
  }, {
    key: 'searchFiles',
    value: function searchFiles(searchParams, cancelToken) {
      var url = this.server + '/api2/search/';
      ......
    }
  }, {
    key: 'searchFileInRepo',
    value: function searchFileInRepo(repoID, q) {
      var url = this.server + '/api/v2.1/search-file/';
      ......
    }
  }

可以看到,在 JS 代码中的这些 API 本质上是直接通过 get 方法,向 django 服务器发送请求,然后再由 django 端完成对后端的调用。

那么,我们到 django 的 router 看看(seahub/urls.py):

......

# search file by name
url(r'^api/v2.1/search-file/$', SearchFile.as_view(), name='api-v2.1-search-file'),

......

# public repos search
url(r'^api/v2.1/published-repo-search/$', PublishedRepoSearchView.as_view(), name='api-v2.1-published-repo-search'),

还有一个,从 API 地址来看,是 /api2/search/,因为有如下语句:

### Apps ###
url(r'^api2/', include('seahub.api2.urls')),

所以我们到 seahub/api2/urls.py 里面去找:

......

url(r'^search/$', Search.as_view(), name='api_search'),

......

(这里想吐槽一下,为啥还用了两套 API 的系统?而且还是用不同风格去写的。。。感觉 API v2.1 是匆忙之下要求尽快上线所以牺牲了一些代码美观程度?

总之,我们先打开了位于 /api2/search/ 中的 Search view,看到了这样的代码:

class Search(APIView):
    """ Search all the repos
    """
    authentication_classes = (TokenAuthentication, SessionAuthentication)
    permission_classes = (IsAuthenticated,)
    throttle_classes = (UserRateThrottle, )

    def get(self, request, format=None):
        if not HAS_FILE_SEARCH:
            error_msg = 'Search not supported.'
            return api_error(status.HTTP_404_NOT_FOUND, error_msg)

        # argument check
        keyword = request.GET.get('q', None)
        if not keyword:
            error_msg = 'q invalid.'
            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

        try:
            current_page = int(request.GET.get('page', '1'))
            per_page = int(request.GET.get('per_page', '10'))
            if per_page > 100:
                per_page = 100
        except ValueError:
            current_page = 1
            per_page = 10

        ......
        
        return Response({"total":total, "results":results, "has_more":has_more})

我们一层一层剥开其中的东西。

首先就是在前面,用 HAS_FILE_SEARCH 判断的,那我们去翻翻:

# search realted
HAS_FILE_SEARCH = False
if EVENTS_CONFIG_FILE:
    def check_search_enabled():
        enabled = False
        if hasattr(seafevents, 'is_search_enabled'):
            enabled = seafevents.is_search_enabled(parsed_events_conf)

            if enabled:
                logging.debug('search: enabled')
            else:
                logging.debug('search: not enabled')
        return enabled

    HAS_FILE_SEARCH = check_search_enabled()

这里开始犯难了,采用了一个 seafevents 来去判断 is_search_enabled,这。。。

seafevents 的难题

这里对 seafevents 可以说。。。是一点也不了解,也没有任何相关字段。我尝试在整个项目中全局搜索 seafevents,找到了这样的线索:

seahub/utils/__init__.py

if EVENTS_CONFIG_FILE:
    parsed_events_conf = configparser.ConfigParser()
    parsed_events_conf.read(EVENTS_CONFIG_FILE)

    try:
        import seafevents
        EVENTS_ENABLED = True
        SeafEventsSession = seafevents.init_db_session_class(EVENTS_CONFIG_FILE)
    except ImportError:
        logging.exception('Failed to import seafevents package.')
        seafevents = None
        EVENTS_ENABLED = False

......

这里可以看到,以 EVENTS_CONFIG_FILE 为判断标准,来导入 seafevents 包。那么,这个 EVENTS_CONFIG_FILE 又是怎么得到的?

在这里,seahub/settings.py

d = os.path.dirname
EVENTS_CONFIG_FILE = os.environ.get(
    'EVENTS_CONFIG_FILE',
    os.path.join(
        d(d(d(d(os.path.abspath(__file__))))), 'conf', 'seafevents.conf'
    )
)

啊,原来是直接检查文件是否存在吗。。。根据网上的资料:

【网盘项目日志】20210526:Seafile 搜索系统开发日志(2)_第1张图片

那目前了解到的情况是:seafevents 是一个 Pro 版本特有的东西,与数据库有关,这东西通过一个配置文件来获得信息。

这样一来,我们没有办法通过 seafevents 来直接判断是否开启搜索了。不过其实。。。还好吧?那么大不了我们添加一个新的接口,通过 seafile API 来直接判断是否支持搜索系统好了。不过这里我们先不动这个代码了,因为后面 pro 的文件里找到了不少咱们需要的东西。

分析缺失的接口

接着看 search 里面的东西。我们知道,主要就是 api2/views.py 中的 Search,api2/endpoints/search_file.py 中的 SearchFile,api2/endpoints/public_repos_search.py 中的 PublishedRepoSearchView,这三个 View。

那么,这些逻辑其实还好啦,主要就是把里面对 SeafileAPI 的调用找出来。

经过查找,找到了这些方法:

  • SearchFile
    • seafile_api.get_repo(repo_id)
    • seafile_api.check_permission_by_path(repo_id, path, username)
    • seafile_api.search_files(repo_id, q)
  • Search
    • seafile_api.get_dir_id_by_path(search_repo, search_path)
    • seafile_api.get_repo(repo_id)
    • seafile_api.search_files(repo_id, q)
    • seahub_extra.search.utils
  • PublishedRepoSearchView
    • seahub_extra.search.utils

这里就发现问题了,seahub_extra。这个是只有 Pro 版本才有的东西,这个就比较麻烦了,连里面有什么都不清楚。不过本着能拿绝不写的原则,我们先去隔壁 pro 版文件里面,看看有没有什么有用的东西。

分析 Pro 包中可以使用的代码

下载到的 pro 包:

哦吼?颤抖的心,点开看看!

舒服了,基本上一个也没少!好家伙!

当机立断,直接把这堆东西全部复制到咱的项目目录去。首先,把 seahub_extra 文件夹放到 seahub 文件夹的同级位置。

【网盘项目日志】20210526:Seafile 搜索系统开发日志(2)_第2张图片

咱们的 PyCharm 编辑器没有识别到,正好介绍一下怎么把其他目录放上去。

打开 PyCharm - Preferences:

【网盘项目日志】20210526:Seafile 搜索系统开发日志(2)_第3张图片

选择 project: seahub - project structure:

右边的 Add Content Root,把 seahub_extra 文件夹也加进去就好啦。

加进去以后简单先浏览一下,发现又有一个叫 seafes 的东西缺失了,而且。。。居然又是套了一层壳。。。

seahub_extra/search/utils.py

os.environ['EVENTS_CONFIG_FILE'] = EVENTS_CONFIG_FILE
from seafes import es_search

# Get an instance of a logger
logger = logging.getLogger(__name__)


# Decoupled from saehub's variable
SEARCH_FILEEXT = {
    TEXT: ('ac', 'am', 'bat', 'c', 'cc', 'cmake', 'cpp', 'cs', 'css', 'diff', 'el', 'h', 'html', 'htm', 'java', 'js', 'json', 'less', 'make', 'org', 'php', 'pl', 'properties', 'py', 'rb', 'scala', 'script', 'sh', 'sql', 'txt', 'text', 'tex', 'vi', 'vim', 'xhtml', 'xml', 'log', 'csv', 'groovy', 'rst', 'patch', 'go'),
    IMAGE: ('gif', 'jpeg', 'jpg', 'png', 'ico', 'bmp', 'tif', 'tiff', 'eps'),
    DOCUMENT: ('doc', 'docx', 'ppt', 'pptx', 'odt', 'fodt', 'odp', 'fodp'),
    SPREADSHEET: ('xls', 'xlsx', 'ods', 'fods'),
    SVG: ('svg',),
    PDF: ('pdf',),
    MARKDOWN: ('markdown', 'md'),
    VIDEO: ('mp4', 'ogv', 'webm', 'mov'),
    AUDIO: ('mp3', 'oga', 'ogg'),
    '3D': ('stl', 'obj'),
}

def get_owned_repos(username, org_id=None):
    ......
    return shared_repos

def get_group_repos(username, org_id=None):
    ......
    return groups_repos

def get_public_repos(username, org_id=None):
    ......
    return public_repos

def get_search_repos_map(search_repo, username, org_id, shared_from, not_shared_from):
	......
	return repo_id_map, repo_type_map

def search_files(repos_map, search_path, keyword, obj_desc, start, size, org_id=None):
    # search file
    if len(repos_map) > 1:
        search_path = None
    files_found, total = es_search(repos_map, search_path, keyword, obj_desc, start, size)
    ......
    return result, total

......

看,在 search_file 里面,又用了 es_search。这应该是 Elastic Search 相关的东西吧?

继续发挥传统艺能:

【网盘项目日志】20210526:Seafile 搜索系统开发日志(2)_第4张图片

打开发现,哇,收获很大啊!甚至连文档都在!

【网盘项目日志】20210526:Seafile 搜索系统开发日志(2)_第5张图片

这个详细程度,堪比保姆级手把手教学了吧?就差把 Elastic Search 也给我讲一遍了!

不过有一点,它这里用的 Elactic Search 并不是公共的 Elastic Search,而是他们自己的版本。而且,他们提供的这个 Github 地址明显存放的是二进制版内容,而打开以后。。。

【网盘项目日志】20210526:Seafile 搜索系统开发日志(2)_第6张图片

这 Git 仓库已经失效了啊。。。不过我在文件夹里发现了它原本让咱们下载的东西:

【网盘项目日志】20210526:Seafile 搜索系统开发日志(2)_第7张图片

打开一看,好家伙,这不就是 17 版的 Elastic Search 原版嘛。。。。。。

【网盘项目日志】20210526:Seafile 搜索系统开发日志(2)_第8张图片

没想到啊没想到,你这浓眉大眼的 Seafile,也是个拿来党(【网盘项目日志】20210526:Seafile 搜索系统开发日志(2)_第9张图片

基本分析完毕,用手上这堆东西把 Elastic Search 系统恢复出来。

对了,seafevents 也在 pro 里面找到了。

刚刚搞到一半才想起来,如果只是把东西放在同目录底下应该是不行哒,得在 $PYTHONPATH 里面也要出现才行。那么,就需要稍微修改一下咱们的启动脚本了。

原本是:

cd ~/dev/source-code/seahub/

export PYTHONPATH=/usr/local/lib/python3.6/site-packages/:/root/dev/source-code/seahub/thirdpart:$PYTHONPATH
export CCNET_CONF_DIR=/root/dev/conf
export SEAFILE_CONF_DIR=/root/dev/seafile-data
export SEAFILE_CENTRAL_CONF_DIR=/root/dev/conf

现在得多加点东西:

export PYTHONPATH=/root/dev/source-code:/root/dev/source-code/pro/python:$PYTHONPATH

总之先把拿到的各种资源整合一下,说不定直接就能用呢(

在网上找到一份 seafevents.conf 的内容,将其放到 /root/dev/conf 下(英文注释是我补的,不知道为啥我那个 vim 无法输入中文,全乱码):

[AUDIT]
enabled = false

[INDEX FILES]
enabled = true

# Update interval for search index. could be s(seconds), m(minutes), d(days)
interval = 10m

# If "true", search index will also handle pdf file content.
# Notice: If you change this option from "false" to "true", you need to clear the search index and index again. For more info, please refer to FAQ.
index_office_pdf = true

[OFFICE CONVERTER]

# To enable pdf and office preview, you must set this settings to "true".
enabled = false
 
# Libreoffice worker threads.
workers = 1
 
# The converted pdf and office file storage location.
outputdir = /tmp/

# Maximum preview pages. Default is 50.
max-pages = 50

# Maximum preview file size, in MB. Default is 2 MB.
max-size = 2

[SEAHUB EMAIL]

# To enable mail notification system, you need to set this to "true"
enabled = false

# Email send interval. Could be s(seconds), m(minutes), d(days)
interval = 30m

开始运行已有代码

OK,我们把环境变量配置好,配置文件也弄好了,现在开始运行。界面一切正常,但是搜索似乎会卡住:

【网盘项目日志】20210526:Seafile 搜索系统开发日志(2)_第10张图片

打开网络控制台,发现是报了 404 错误:

【网盘项目日志】20210526:Seafile 搜索系统开发日志(2)_第11张图片

这个根据咱们的代码段判断,应该是 seafevents 没有加载成功。后来翻开日志,发现果然:

2021-05-27 07:13:31,294 [ERROR] root:69  Failed to import seafevents package.
Traceback (most recent call last):
  File "/root/dev/source-code/seahub/seahub/utils/__init__.py", line 66, in 
    from seafevents import seafevents_api
  File "/root/dev/source-code/pro/python/seafevents/__init__.py", line 43, in 
    from .virus_scanner import get_virus_files, delete_virus_file, operate_virus_file, \
  File "/root/dev/source-code/pro/python/seafevents/virus_scanner/__init__.py", line 2, in 
    from .virus_scan import VirusScan
  File "/root/dev/source-code/pro/python/seafevents/virus_scanner/virus_scan.py", line 6, in 
    from seafobj import commit_mgr, fs_mgr, block_mgr
ModuleNotFoundError: No module named 'seafobj'
2021-05-27 07:13:31,317 [ERROR] root:562  Failed to import seafevents package.
Traceback (most recent call last):
  File "/root/dev/source-code/seahub/seahub/utils/__init__.py", line 558, in 
    import seafevents
  File "/root/dev/source-code/pro/python/seafevents/__init__.py", line 43, in 
    from .virus_scanner import get_virus_files, delete_virus_file, operate_virus_file, \
  File "/root/dev/source-code/pro/python/seafevents/virus_scanner/__init__.py", line 2, in 
    from .virus_scan import VirusScan
  File "/root/dev/source-code/pro/python/seafevents/virus_scanner/virus_scan.py", line 6, in 
    from seafobj import commit_mgr, fs_mgr, block_mgr

看起来,seafevents 是成功加载了,但是没有完全加载。由于 seafobj 没有找到,这玩意炸掉了。

啊,这好说,我们的项目组正好 fork 了一份 seafboj 的库,我们把它 clone 下来,然后把里面的 seafobj 子文件夹添加在 seahub/thirdpart 文件夹中。

然后就又愉快的报错了。

Traceback (most recent call last):
  File "/root/dev/source-code/pro/python/seafevents/db.py", line 112, in init_db_session_class
    engine = create_engine_from_conf(config_file, db)
  File "/root/dev/source-code/pro/python/seafevents/db.py", line 52, in create_engine_from_conf
    backend = config.get(db_sec, 'type')
  File "/usr/lib/python3.6/configparser.py", line 781, in get
    d = self._unify_values(section, vars)
  File "/usr/lib/python3.6/configparser.py", line 1141, in _unify_values
    raise NoSectionError(section)
configparser.NoSectionError: No section: 'DATABASE'

数据库嘛。。。看来提供的配置文件里还少了一内内东西,我们再找点资料来。对不起,我是憨憨,明明在 pro 里面就有现成的 conf 文件,我非要去网上找。。。

[DATABASE]
type = mysql
host = db
port = 3306
username = seafile
password = 7e8ed7e1-c4e8-4bc1-97ae-42a83fff9761
name = seahub_db

[AUDIT]
enabled = false

[INDEX FILES]
external_es_server = true
es_host = elasticsearch
es_port = 9200
enabled = true
interval = 10m

highlight = fvh

## If true, indexes the contents of office/pdf files while updating search index
## Note: If you change this option from "false" to "true", then you need to clear the search index and update the index again. See the FAQ for details.
index_office_pdf = true

[OFFICE CONVERTER]
port = 6000
host = 127.0.0.1
enabled = false
workers = 1

[SEAHUB EMAIL]
enabled = false

## interval of sending Seahub email. Can be s(seconds), m(minutes), h(hours), d(days)
interval = 30m

# Enable statistics
[STATISTICS]
enabled=false

注意需要把上面的数据库选项改成咱们自己的。

然后又启动,又报错了。

RuntimeError: ('invalid config file %s', '/root/dev/conf/seafile.conf')

结果一看,奇怪,这个配置文件有点眼熟啊。。。

【网盘项目日志】20210526:Seafile 搜索系统开发日志(2)_第12张图片

看,社区版当时做的时候,把这个文件放在了 seafile-data 里面。我们给他纠正一下,也放到 conf 里面。不过 Seafile-Server 要想运行还是需要它在 seafile-data 里面(别问我为什么,这句话是我后面报错以后又回来加的,弄得我很恼火),那么我打算把原件放在 conf 中,然后一个软链接链到 seafile-data 里面。

mv seafile.conf ../conf
ln -s ../conf/seafile.conf ./

改动完了啦,结果又出了问题:

sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1698, "Access denied for user 'root'@'localhost'")

连不上数据库???但是咱们用相同配置文件都能让 seafile-server 运行起来啊???怎么到了你 python 这里就萎了?

这里考虑到 root 用户有些特殊性,因此在数据库内创建了一个 master 账户,并赋予权限,之后把配置文件也改了。

现在,再试一次,错误终于换了啦。

RuntimeError: seafesdir is not set

看来是 Elastic Search 的文件夹没有被设置好。找到提示的对应代码:

# [ seafesdir ]
seafesdir = get_opt_from_conf_or_env(config, section_name, key_seafesdir, 'SEAFES_DIR', None)

看来既可以在环境变量中设置,也可以在配置文件中设置。那么为了方便起见,我们直接在环境变量中设置。他要的这个 seafes,应该就是 pro/python/seafes,当时咱们看里面内容的时候,还有一些脚本和文档,应该就是那个。使用以下命令添加环境变量:

export SEAFES_DIR=/root/dev/source-code/pro/python/seafes

再运行,终于,看到了久违的运行成功提示。现在我们来到高级搜索界面,然后输入内容查找,看到了 Internal Server Error。打开日志,发现新错误:

urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno -2] Name or service not known
2021-05-27 09:54:03,560 [WARNING] elasticsearch:97 log_request_fail HEAD http://elasticsearch:9200/repofiles [status:N/A request:0.008s]

看来,是 Elastic Search 的服务器找不到。意料之中,因为。。。。。。我确实没开 Elastic Search(

那也就是说,基本上 Django 这边没啥问题了。那么,下一阶段我们把 Elastic Search 整出来。

你可能感兴趣的:(山东大学网盘开发)