天天看點

【網盤項目日志】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)

那目前了解到的情況是: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 包:

【網盤項目日志】20210526:Seafile 搜尋系統開發日志(2)

哦吼?顫抖的心,點開看看!

【網盤項目日志】20210526:Seafile 搜尋系統開發日志(2)

舒服了,基本上一個也沒少!好家夥!

當機立斷,直接把這堆東西全部複制到咱的項目目錄去。首先,把 seahub_extra 檔案夾放到 seahub 檔案夾的同級位置。

【網盤項目日志】20210526:Seafile 搜尋系統開發日志(2)

咱們的 PyCharm 編輯器沒有識别到,正好介紹一下怎麼把其他目錄放上去。

打開 PyCharm - Preferences:

【網盤項目日志】20210526:Seafile 搜尋系統開發日志(2)

選擇 project: seahub - project structure:

【網盤項目日志】20210526:Seafile 搜尋系統開發日志(2)

右邊的 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)

打開發現,哇,收獲很大啊!甚至連文檔都在!

【網盤項目日志】20210526:Seafile 搜尋系統開發日志(2)
【網盤項目日志】20210526:Seafile 搜尋系統開發日志(2)

這個詳細程度,堪比保姆級手把手教學了吧?就差把 Elastic Search 也給我講一遍了!

不過有一點,它這裡用的 Elactic Search 并不是公共的 Elastic Search,而是他們自己的版本。而且,他們提供的這個 Github 位址明顯存放的是二進制版内容,而打開以後。。。

【網盤項目日志】20210526:Seafile 搜尋系統開發日志(2)

這 Git 倉庫已經失效了啊。。。不過我在檔案夾裡發現了它原本讓咱們下載下傳的東西:

【網盤項目日志】20210526:Seafile 搜尋系統開發日志(2)

打開一看,好家夥,這不就是 17 版的 Elastic Search 原版嘛。。。。。。

【網盤項目日志】20210526:Seafile 搜尋系統開發日志(2)

沒想到啊沒想到,你這濃眉大眼的 Seafile,也是個拿來黨(

【網盤項目日志】20210526:Seafile 搜尋系統開發日志(2)

基本分析完畢,用手上這堆東西把 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
           

現在得多加點東西:

總之先把拿到的各種資源整合一下,說不定直接就能用呢(

在網上找到一份 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)

打開網絡控制台,發現是報了 404 錯誤:

【網盤項目日志】20210526:Seafile 搜尋系統開發日志(2)

這個根據咱們的代碼段判斷,應該是 seafevents 沒有加載成功。後來翻開日志,發現果然:

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

看,社群版當時做的時候,把這個檔案放在了 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: <urllib3.connection.HTTPConnection object at 0x7f161a5d9ef0>: 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 整出來。