默默呆,繼續做搜尋系統啊
文章目錄
-
- 研究一下已經給出的前端檔案
- 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'
)
)
啊,原來是直接檢查檔案是否存在嗎。。。根據網上的資料:

那目前了解到的情況是: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 檔案夾的同級位置。
咱們的 PyCharm 編輯器沒有識别到,正好介紹一下怎麼把其他目錄放上去。
打開 PyCharm - Preferences:
選擇 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 相關的東西吧?
繼續發揮傳統藝能:
打開發現,哇,收獲很大啊!甚至連文檔都在!
這個詳細程度,堪比保姆級手把手教學了吧?就差把 Elastic Search 也給我講一遍了!
不過有一點,它這裡用的 Elactic Search 并不是公共的 Elastic Search,而是他們自己的版本。而且,他們提供的這個 Github 位址明顯存放的是二進制版内容,而打開以後。。。
這 Git 倉庫已經失效了啊。。。不過我在檔案夾裡發現了它原本讓咱們下載下傳的東西:
打開一看,好家夥,這不就是 17 版的 Elastic Search 原版嘛。。。。。。
沒想到啊沒想到,你這濃眉大眼的 Seafile,也是個拿來黨(
基本分析完畢,用手上這堆東西把 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,我們把環境變量配置好,配置檔案也弄好了,現在開始運作。界面一切正常,但是搜尋似乎會卡住:
打開網絡控制台,發現是報了 404 錯誤:
這個根據咱們的代碼段判斷,應該是 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')
結果一看,奇怪,這個配置檔案有點眼熟啊。。。
看,社群版當時做的時候,把這個檔案放在了 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 整出來。