├── gateway ├── src │ └── main │ │ ├── webapp │ │ ├── vms │ │ │ ├── readme.md │ │ │ ├── render_issue.vm │ │ │ ├── render_repo.vm │ │ │ └── render_code.vm │ │ ├── favicon.ico │ │ ├── img │ │ │ ├── logo.png │ │ │ └── kooder_logo.png │ │ ├── layout │ │ │ └── html.vm │ │ ├── index.vm │ │ └── css │ │ │ └── main.css │ │ ├── resources │ │ ├── mime-types.properties │ │ ├── velocity.properties │ │ └── simplelogger.properties │ │ └── java │ │ └── com │ │ └── gitee │ │ └── kooder │ │ ├── examples │ │ ├── CollectorExample.java │ │ └── GroupingSearchExample.java │ │ ├── action │ │ ├── IndexAction.java │ │ ├── GiteaAction.java │ │ ├── GiteeAction.java │ │ ├── TaskAction.java │ │ └── SearchActionBase.java │ │ ├── server │ │ ├── Tester.java │ │ ├── AutoContentTypeStaticHandler.java │ │ ├── Action.java │ │ ├── GatewayBase.java │ │ └── Gateway.java │ │ └── SearchCmd.java ├── readme.md └── pom.xml ├── core ├── src │ └── main │ │ ├── resources │ │ ├── lexicon │ │ │ ├── lex-tech.lex │ │ │ └── readme.md │ │ ├── languages │ │ ├── simplelogger.properties │ │ └── jcseg.properties │ │ └── java │ │ └── com │ │ └── gitee │ │ └── kooder │ │ ├── index │ │ └── IndexException.java │ │ ├── query │ │ ├── QueryException.java │ │ ├── QueryFactory.java │ │ ├── IQuery.java │ │ ├── IssueQuery.java │ │ └── CodeQuery.java │ │ ├── utils │ │ ├── LanguageQuote.java │ │ ├── FileClassifierResult.java │ │ ├── BatchTaskRunner.java │ │ ├── HttpUtils.java │ │ └── JsonUtils.java │ │ ├── queue │ │ ├── Queue.java │ │ ├── QueueProvider.java │ │ ├── QueueFactory.java │ │ ├── EmbedQueueProvider.java │ │ └── RedisQueueProvider.java │ │ ├── code │ │ ├── RepositoryProvider.java │ │ ├── FileTraveler.java │ │ ├── SvnRepositoryProvider.java │ │ ├── FileRepositoryProvider.java │ │ ├── RepositoryFactory.java │ │ ├── TechCodeAnalyzer.java │ │ ├── SourceCodeAnalyzer.java │ │ ├── CodeFileTraveler.java │ │ └── RepositoryManager.java │ │ ├── models │ │ ├── CodeLine.java │ │ ├── Relation.java │ │ ├── CodeOwner.java │ │ └── Searchable.java │ │ ├── jcseg │ │ ├── JcsegAnalyzer.java │ │ ├── JcsegTokenizerFactory.java │ │ └── JcsegTokenizer.java │ │ ├── storage │ │ ├── IndexStorage.java │ │ └── StorageFactory.java │ │ └── core │ │ ├── SearchKey.java │ │ ├── KooderConfig.java │ │ └── Constants.java └── readme.md ├── .gitignore ├── bin ├── scmd.sh ├── gsimport.sh ├── scmd.bat ├── indexer.sh ├── gateway.sh ├── gsimport.bat ├── gateway.bat ├── indexer.bat ├── gatewaydocker.sh └── service.sh ├── docs ├── img │ ├── screenshot.png │ ├── gitea_webhook.png │ ├── gsearch-flow.png │ ├── kooder-index.png │ ├── docker-ha-kooder.png │ ├── gsearch-indexer.png │ ├── gitea_webhook_select.png │ ├── gitea_webhook_setting.png │ ├── personal_access_token.png │ └── personal_access_token_1.png ├── gsearch-dataflow.eddx ├── gsearch-indexer-flow.eddx ├── gsearch-indexer-thread.eddx ├── TODO.md ├── roadmap.md ├── API.md └── configuration.md ├── docker-compose.yaml ├── indexer ├── readme.md ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── gitee │ │ │ └── kooder │ │ │ ├── gitea │ │ │ ├── GiteaWebHookEvent.java │ │ │ ├── GiteaException.java │ │ │ ├── RepositoryWebHook.java │ │ │ ├── IssueWebHook.java │ │ │ ├── PushWebHook.java │ │ │ ├── Organization.java │ │ │ └── User.java │ │ │ ├── gitee │ │ │ ├── GiteeException.java │ │ │ ├── EnterpriseHook.java │ │ │ ├── PushWebHook.java │ │ │ ├── IssueWebHook.java │ │ │ ├── GiteeWebHookEvent.java │ │ │ ├── Enterprise.java │ │ │ ├── User.java │ │ │ ├── RepoWebHook.java │ │ │ ├── Label.java │ │ │ └── Issue.java │ │ │ ├── indexer │ │ │ ├── ServerDaemon.java │ │ │ ├── Gitlab.java │ │ │ └── RepositoryIndexer.java │ │ │ └── file │ │ │ └── FileIndexThread.java │ │ └── resources │ │ └── simplelogger.properties └── pom.xml ├── Dockerfile ├── docker-compose-ha.yaml ├── haproxy.cfg ├── kooder.properties └── pom.xml /gateway/src/main/webapp/vms/readme.md: -------------------------------------------------------------------------------- 1 | 该目录用来存放 velocity 模板被嵌套到其他页面中的小组件 -------------------------------------------------------------------------------- /core/src/main/resources/lexicon/lex-tech.lex: -------------------------------------------------------------------------------- 1 | CJK_WORD 2 | 小程序/n/xiao cheng xu/null -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*/target/ 2 | data 3 | lib/*.jar 4 | lib/*.pom 5 | .DS_Store 6 | *.iml 7 | .idea -------------------------------------------------------------------------------- /bin/scmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | java -cp lib/*:gateway/target/classes com.gitee.kooder.SearchCmd $* -------------------------------------------------------------------------------- /core/src/main/resources/lexicon/readme.md: -------------------------------------------------------------------------------- 1 | 该目录存放了 jcseg 自定义的词库信息,如果有对默认的词库有改动可以复制到此目录然后进行修改。 -------------------------------------------------------------------------------- /docs/img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/docs/img/screenshot.png -------------------------------------------------------------------------------- /docs/gsearch-dataflow.eddx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/docs/gsearch-dataflow.eddx -------------------------------------------------------------------------------- /docs/img/gitea_webhook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/docs/img/gitea_webhook.png -------------------------------------------------------------------------------- /docs/img/gsearch-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/docs/img/gsearch-flow.png -------------------------------------------------------------------------------- /docs/img/kooder-index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/docs/img/kooder-index.png -------------------------------------------------------------------------------- /bin/gsimport.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | java -cp lib/*:indexer/target/classes com.gitee.kooder.indexer.PathImporter $* -------------------------------------------------------------------------------- /bin/scmd.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | java -cp lib\*;gateway/target/classes com.gitee.kooder.SearchCmd %1 %2 %3 %4 %5 %6 -------------------------------------------------------------------------------- /docs/img/docker-ha-kooder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/docs/img/docker-ha-kooder.png -------------------------------------------------------------------------------- /docs/img/gsearch-indexer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/docs/img/gsearch-indexer.png -------------------------------------------------------------------------------- /bin/indexer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ava -cp lib/*:indexer/target/classes com.gitee.kooder.indexer.ServerDaemon $* -------------------------------------------------------------------------------- /docs/gsearch-indexer-flow.eddx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/docs/gsearch-indexer-flow.eddx -------------------------------------------------------------------------------- /bin/gateway.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | java -cp lib/*:gateway/target/classes com.gitee.kooder.server.Gateway %1 %2 %3 %4 %5 %6 -------------------------------------------------------------------------------- /docs/gsearch-indexer-thread.eddx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/docs/gsearch-indexer-thread.eddx -------------------------------------------------------------------------------- /docs/img/gitea_webhook_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/docs/img/gitea_webhook_select.png -------------------------------------------------------------------------------- /docs/img/gitea_webhook_setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/docs/img/gitea_webhook_setting.png -------------------------------------------------------------------------------- /docs/img/personal_access_token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/docs/img/personal_access_token.png -------------------------------------------------------------------------------- /bin/gsimport.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | java -cp lib\*;indexer/target/classes com.gitee.kooder.indexer.PathImporter %1 %2 %3 %4 %5 %6 -------------------------------------------------------------------------------- /docs/img/personal_access_token_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/docs/img/personal_access_token_1.png -------------------------------------------------------------------------------- /gateway/src/main/webapp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/gateway/src/main/webapp/favicon.ico -------------------------------------------------------------------------------- /gateway/src/main/webapp/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/gateway/src/main/webapp/img/logo.png -------------------------------------------------------------------------------- /bin/gateway.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | java -Xmx1024m -cp lib\*;gateway/target/classes com.gitee.kooder.server.Gateway %1 %2 %3 %4 %5 %6 -------------------------------------------------------------------------------- /bin/indexer.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | java -Xmx1024m -cp lib\*;indexer/target/classes com.gitee.kooder.indexer.ServerDaemon %1 %2 %3 %4 %5 %6 -------------------------------------------------------------------------------- /gateway/src/main/webapp/img/kooder_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschina/kooder/HEAD/gateway/src/main/webapp/img/kooder_logo.png -------------------------------------------------------------------------------- /core/src/main/resources/languages: -------------------------------------------------------------------------------- 1 | #编程语言定义,用于搜索关键字的提取 2 | #每一行一个编程语言,使用/隔开多个表达形式 3 | #\#开头为注释 4 | java 5 | javascript/js 6 | php 7 | python 8 | c#/csharp 9 | go/golang -------------------------------------------------------------------------------- /bin/gatewaydocker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | java -cp lib/*:gateway/target/classes -Dkooder.properties=/root/kooder.properties com.gitee.kooder.server.Gateway %1 %2 %3 %4 %5 %6 3 | -------------------------------------------------------------------------------- /docs/TODO.md: -------------------------------------------------------------------------------- 1 | ### Kooder TODO 2 | 3 | 1. 测试 Redis 队列 4 | 2. 解决 jcseg 在多线程环境下的分词异常 5 | 3. 梳理整个流程的异常并进行相应记录和处理 6 | 4. 大数据量下的性能优化 7 | 5. 支持指定企业,指定仓库的代码检索 DONE 8 | 6. 支持 Gitee Premium 9 | 7. 支持 Gitea 10 | 8. 支持多个 Git 服务 -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | kooder: 5 | image: registry.cn-hangzhou.aliyuncs.com/devops_hu/kooder:v1.0.1 6 | container_name: kooder 7 | volumes: 8 | - ./kooder.properties:/root/kooder.properties:ro 9 | ports: 10 | - 8080:8080 11 | -------------------------------------------------------------------------------- /indexer/readme.md: -------------------------------------------------------------------------------- 1 | ### Gitee Search Indexer 2 | 3 | indexer 用来接收来自包括数据库、消息队列、Gateway 请求等渠道的索引构建、更新、删除的命令, 4 | 并将更新到索引库中。 5 | 6 | indexer 自身维护一个持久化队列,可根据实际索引任务量来配置并发处理线程。 7 | 由于索引库不支持多线程同时写入,为了确保最大的吞吐量,对此块的设计需要认真考虑。 8 | 9 | #### indexer 多线程模型 10 | 11 | ![Gitee Search Indexer Flow](../docs/img/gsearch-indexer.png) -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.cn-hangzhou.aliyuncs.com/devops_hu/maven:3.5.2 2 | 3 | ARG USER_HOME_DIR="/root" 4 | 5 | WORKDIR ${USER_HOME_DIR} 6 | 7 | COPY . . 8 | 9 | RUN chmod 755 ${USER_HOME_DIR}/bin/*.sh \ 10 | && cd $USER_HOME_DIR \ 11 | && mvn install \ 12 | && rm -rf /tmp/* /var/cache/apk/* 13 | 14 | EXPOSE 8080 15 | 16 | CMD ["/root/bin/gatewaydocker.sh"] 17 | -------------------------------------------------------------------------------- /core/readme.md: -------------------------------------------------------------------------------- 1 | ### Gitee Search Core 2 | 3 | `Core` 是 Gitee Search 的核心模块,包含底层结构定义、索引处理、队列以及通用工具包。 4 | 5 | #### 源码包说明 6 | 7 | `com.gitee.kooder.code` 代码搜索相关 8 | `com.gitee.kooder.core` 搜索核心封装 9 | `com.gitee.kooder.index` 构建索引 10 | `com.gitee.kooder.jcseg` jcseg 分词 11 | `com.gitee.kooder.query` 搜索封装 12 | `com.gitee.kooder.queue` 队列封装 13 | `com.gitee.kooder.storage` 索引存储 14 | `com.gitee.kooder.utils` 工具包 -------------------------------------------------------------------------------- /gateway/src/main/resources/mime-types.properties: -------------------------------------------------------------------------------- 1 | # mime types 2 | 3 | html = text/html; charset=UTF-8 4 | htm = text/html; charset=UTF-8 5 | js = text/javascript; charset=UTF-8 6 | css = text/css; charset=UTF-8 7 | png = image/png 8 | jpg = image/jpeg 9 | gif = image/gif 10 | ico = application/x-ico 11 | webp = image/webp 12 | json = application/json 13 | bmp = application/x-bmp 14 | txt = text/plain; charset=UTF-8 15 | woff = application/font-woff -------------------------------------------------------------------------------- /docs/roadmap.md: -------------------------------------------------------------------------------- 1 | ## Kooder 路线图 2 | 3 | 代码就是数据,Kooder 希望成为一个深入的从企业代码资产中挖掘出代码本身的数据价值,包括开发能效、安全管控、代码审计等。 4 | 同时我们还希望 Kooder 能成为企业代码深度语义分析的引擎,提升智能编码能力,提升开发效率。 5 | 6 | Kooder 下一步工作: 7 | 8 | ### 代码即数据 9 | 10 | * 对接多种代码源(Gitea、SVN 等) 11 | * 对接企业多个代码源,实现统一的代码数据管理 12 | * 实现代码搜索的权限管理,使用者只能搜索其有权限的仓库代码 13 | * 提供代码敏感信息告警能力 14 | 15 | ### 提升工程效能 16 | 17 | * 根据人员、时间段进行代码统计,量化开发人员贡献度 18 | 19 | ### 代码智能推荐系统 20 | 21 | * 实现对代码的语义分析和索引 22 | * 实现 Web 代码浏览时的引用跳转 23 | * 代码查重 24 | * 智能代码推荐 25 | -------------------------------------------------------------------------------- /gateway/src/main/webapp/vms/render_issue.vm: -------------------------------------------------------------------------------- 1 | ## 搜索issue结果渲染 2 | -------------------------------------------------------------------------------- /gateway/src/main/resources/velocity.properties: -------------------------------------------------------------------------------- 1 | # velocity properties 2 | default.contentType=text/html 3 | 4 | resource.default_encoding = UTF-8 5 | 6 | resource.loaders = file 7 | 8 | resource.loader.file.path = gateway/src/main/webapp 9 | resource.loader.file.cache = true 10 | resource.loader.file.modification_check_interval = 2 11 | introspector.uberspect.class = org.apache.velocity.util.introspection.UberspectImpl, org.apache.velocity.util.introspection.UberspectPublicFields 12 | 13 | directive.set.null.allowed = true 14 | directive.parse.max_depth = 10 -------------------------------------------------------------------------------- /docker-compose-ha.yaml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | koodera: 5 | image: registry.cn-hangzhou.aliyuncs.com/devops_hu/kooder:v1.0.1 6 | expose: 7 | - 8080 8 | kooderb: 9 | build: . 10 | expose: 11 | - 8080 12 | 13 | haproxy: 14 | image: haproxy:1.6 15 | volumes: 16 | - ./kooder.properties:/root/kooder.properties:ro 17 | - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro 18 | links: 19 | - koodera 20 | - kooderb 21 | ports: 22 | - "8080:8080" 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /gateway/src/main/webapp/layout/html.vm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $page_title 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ${screen_content} 14 | 15 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitea/GiteaWebHookEvent.java: -------------------------------------------------------------------------------- 1 | package com.gitee.kooder.gitea; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @author zhanggx 7 | */ 8 | public enum GiteaWebHookEvent { 9 | 10 | REPOSITORY_HOOK("repository"), 11 | PUSH_HOOK("push"), 12 | ISSUES_HOOK("issues"); 13 | 14 | private String event; 15 | 16 | GiteaWebHookEvent(String event) { 17 | this.event = event; 18 | } 19 | 20 | public static GiteaWebHookEvent getEvent(String event) { 21 | return Arrays.stream(values()) 22 | .filter(giteaWebHookEvent -> giteaWebHookEvent.event.equals(event)) 23 | .findFirst() 24 | .orElse(null); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /gateway/readme.md: -------------------------------------------------------------------------------- 1 | ### Kooder Gateway 2 | 3 | `Gateway` 是 Kooder 对外提供服务的接口(HTTP),通过该接口可以对索引进行管理,以及执行常规的搜索操作。 4 | 5 | #### 接口说明 6 | 7 | **索引管理接口列表** 8 | 9 | * `/task/repositories` 仓库索引管理接口 10 | * `/task/issues` Issue 索引管理接口 11 | * `/task/codes` 代码索引管理接口 12 | 13 | **搜索接口列表** 14 | 15 | * `/search/repositories` 仓库搜索 16 | * `/search/issues` Issue搜索 17 | * `/search/codes` 代码搜索 18 | 19 | **系统管理接口列表** 20 | 21 | * `/metrics/keywords` 获取热门关键字 22 | * `/metrics/storage` Kooder 的存储信息 23 | 24 | #### HTTP 状态码说明 25 | 26 | * `200` 执行成功,响应内容直接存放在 HTTP BODY 27 | * `400` HTTP 请求参数错误,例如 type 参数值错误 28 | * `403` action 方法受保护,不允许调用 29 | * `404` 请求的地址不存在对应的 action 方法 30 | * `406` HTTP 请求内容格式错误,一般是 json 格式有误 31 | * `500` action 执行异常 32 | 33 | #### 源码包说明 34 | 35 | `com.gitee.kooder.action` 处理接口业务逻辑 36 | `com.gitee.kooder.server` HTTP服务 37 | -------------------------------------------------------------------------------- /haproxy.cfg: -------------------------------------------------------------------------------- 1 | global 2 | log 127.0.0.1 local0 3 | log 127.0.0.1 local1 notice 4 | maxconn 4096 5 | defaults 6 | log global 7 | mode http 8 | option httplog 9 | option dontlognull 10 | timeout connect 5000ms 11 | timeout client 50000ms 12 | timeout server 50000ms 13 | listen stats 14 | bind 0.0.0.0:70 15 | mode http 16 | stats enable 17 | stats hide-version 18 | stats scope . 19 | stats realm Haproxy\ Statistics 20 | stats uri / 21 | stats auth user:pass 22 | frontend balancer 23 | bind 0.0.0.0:8080 24 | mode http 25 | default_backend web_backends 26 | backend web_backends 27 | mode http 28 | option forwardfor 29 | balance roundrobin 30 | server koodera koodera:8080 check 31 | server kooderb kooderb:8080 check 32 | option httpchk GET / 33 | http-check expect status 200 34 | -------------------------------------------------------------------------------- /gateway/src/main/webapp/vms/render_repo.vm: -------------------------------------------------------------------------------- 1 | ## 搜索仓库结果渲染 2 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitea/GiteaException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.gitea; 17 | 18 | /** 19 | * Gitea related exception 20 | * 21 | * @author zhanggx 22 | */ 23 | public class GiteaException extends Exception { 24 | 25 | public GiteaException(String message) { 26 | super(message); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitee/GiteeException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.gitee; 17 | 18 | /** 19 | * Gitee related exception 20 | * 21 | * @author zhanggx 22 | */ 23 | public class GiteeException extends Exception { 24 | 25 | public GiteeException(String message) { 26 | super(message); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /docs/API.md: -------------------------------------------------------------------------------- 1 | ### Kooder Search API 2 | 3 | **搜索接口** 4 | 5 | GET(POST): /search/repositories #仓库搜索 6 | 7 | |参数名 |参数含义 | 示例| 8 | --- | --- | --- 9 | |q|搜索关键字|q=password| 10 | |lang|指定编程语言(不支持多值)|lang=Java| 11 | |e.id|搜索指定企业的仓库(仅限 gitee)|e.id=1213| 12 | |sort|排序方法(stars,forks,update)|sort=update| 13 | |p|页码(每页20条)|p=3| 14 | 15 | GET(POST): /search/codes #代码搜索 16 | 17 | |参数名 |参数含义 | 示例| 18 | --- | --- | --- 19 | |q|搜索关键字|q=password| 20 | |lang|指定编程语言(不支持多值)|lang=Java| 21 | |e.id|搜索指定企业的仓库(仅限 gitee)|e.id=1213| 22 | |repo.id|搜索指定仓库的代码,支持多值,使用逗号隔开|repo.id=1213,32| 23 | |sort|排序方法(stars,forks,update)|sort=update| 24 | |p|页码(每页20条)|p=3| 25 | 26 | GET(POST): /search/issues #Issue 搜索 27 | 28 | |参数名 |参数含义 | 示例| 29 | --- | --- | --- 30 | |q|搜索关键字|q=password| 31 | |e.id|搜索指定企业的仓库(仅限 gitee)|e.id=1213| 32 | |sort|排序方法(create,update)|sort=update| 33 | |p|页码(每页20条)|p=3| 34 | 35 | 36 | **WebHook 回调接口** 37 | 38 | /gitlab/system # Gitlab 系统回调接口 39 | /gitlab/project # Gitlab 仓库回调接口 40 | /gitee # Gitee Premium 回调接口 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/index/IndexException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.index; 17 | 18 | /** 19 | * Lucene Index Exception 20 | * @author Winter Lau 21 | */ 22 | public class IndexException extends RuntimeException { 23 | 24 | public IndexException(String message, Throwable cause) { 25 | super(message, cause); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/query/QueryException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.query; 17 | 18 | /** 19 | * Lucene Query Exception 20 | * @author Winter Lau 21 | */ 22 | public class QueryException extends RuntimeException { 23 | 24 | public QueryException(String message, Throwable cause) { 25 | super(message, cause); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitee/EnterpriseHook.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.gitee; 17 | 18 | /** 19 | * @author zhanggx 20 | */ 21 | public class EnterpriseHook { 22 | 23 | private String url; 24 | 25 | public String getUrl() { 26 | return url; 27 | } 28 | 29 | public void setUrl(String url) { 30 | this.url = url; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitee/PushWebHook.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.gitee; 17 | 18 | /** 19 | * @author zhanggx 20 | */ 21 | public class PushWebHook { 22 | 23 | private Repository repository; 24 | 25 | public Repository getRepository() { 26 | return repository; 27 | } 28 | 29 | public void setRepository(Repository repository) { 30 | this.repository = repository; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /gateway/src/main/webapp/index.vm: -------------------------------------------------------------------------------- 1 | #set($layout = 'html.vm') 2 | #set($page_title = 'Kooder -- Search in Code') 3 |

4 |
5 |
6 |
7 |

8 |
9 | 10 |
11 | 12 | 13 |
14 |
15 |

Source @ https://gitee.com/koode/kooder

16 |
17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /gateway/src/main/webapp/vms/render_code.vm: -------------------------------------------------------------------------------- 1 | ## 搜索代码结果渲染 2 | 3 | -------------------------------------------------------------------------------- /indexer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | com.gitee.koode 7 | kooder 8 | 1.0-beta4 9 | 10 | 11 | 4.0.0 12 | 13 | kooder-indexer 14 | 15 | Kooder - indexer 16 | 17 | 18 | 19 | 20 | com.gitee.koode 21 | kooder-core 22 | 1.0-beta4 23 | 24 | 25 | 26 | commons-daemon 27 | commons-daemon 28 | 1.2.3 29 | 30 | 31 | 32 | commons-cli 33 | commons-cli 34 | 1.4 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/utils/LanguageQuote.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.utils; 17 | 18 | /** 19 | * 对应 languages.json 文件中的 quote 字段 20 | */ 21 | public class LanguageQuote { 22 | 23 | public String start; 24 | public String end; 25 | public boolean ignoreescape; 26 | public boolean docstring; 27 | 28 | public LanguageQuote(){} 29 | 30 | public LanguageQuote(String start, String end, boolean ignoreescape, boolean docstring) { 31 | this.start = start; 32 | this.end = end; 33 | this.ignoreescape = ignoreescape; 34 | this.docstring = docstring; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/queue/Queue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.queue; 17 | 18 | import java.util.Collection; 19 | import java.util.List; 20 | 21 | /** 22 | * 定义了获取索引任务的队列接口 23 | * @author Winter Lau 24 | */ 25 | public interface Queue extends AutoCloseable{ 26 | 27 | /** 28 | * 队列的唯一名称 29 | * @return 30 | */ 31 | String type(); 32 | 33 | /** 34 | * 添加任务到队列 35 | * @param tasks 36 | */ 37 | void push(Collection tasks) ; 38 | 39 | /** 40 | * 从队列获取任务 41 | * @return 42 | */ 43 | List pop(int count) ; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/code/RepositoryProvider.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.code; 17 | 18 | import com.gitee.kooder.models.CodeRepository; 19 | 20 | /** 21 | * 代码源接口定义 22 | * @author Winter Lau 23 | */ 24 | public interface RepositoryProvider { 25 | 26 | String name(); 27 | 28 | /** 29 | * 更新仓库 30 | * @param repo 31 | * @param traveler 32 | * @return 返回索引的文件数 33 | */ 34 | int pull(CodeRepository repo, FileTraveler traveler); 35 | 36 | /** 37 | * 删除仓库 38 | * @param repo 39 | * @exception 40 | */ 41 | void delete(CodeRepository repo) ; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitee/IssueWebHook.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.gitee; 17 | 18 | /** 19 | * @author zhanggx 20 | */ 21 | public class IssueWebHook { 22 | 23 | private Issue issue; 24 | 25 | private Repository repository; 26 | 27 | public Issue getIssue() { 28 | return issue; 29 | } 30 | 31 | public void setIssue(Issue issue) { 32 | this.issue = issue; 33 | } 34 | 35 | public Repository getRepository() { 36 | return repository; 37 | } 38 | 39 | public void setRepository(Repository repository) { 40 | this.repository = repository; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/gitee/kooder/examples/CollectorExample.java: -------------------------------------------------------------------------------- 1 | package com.gitee.kooder.examples; 2 | 3 | import com.gitee.kooder.storage.StorageFactory; 4 | import org.apache.lucene.index.IndexReader; 5 | import org.apache.lucene.search.*; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * Collector example 13 | * @author Winter Lau 14 | */ 15 | public class CollectorExample { 16 | 17 | private final static Logger log = LoggerFactory.getLogger(CollectorExample.class); 18 | 19 | public static void main(String[] args) throws IOException { 20 | try (IndexReader reader = StorageFactory.getIndexReader("repo")) { 21 | IndexSearcher searcher = new IndexSearcher(reader); 22 | long ct = System.currentTimeMillis(); 23 | searcher.search(new MatchAllDocsQuery(), new SimpleCollector() { 24 | @Override 25 | public ScoreMode scoreMode() { 26 | return ScoreMode.COMPLETE; 27 | } 28 | 29 | @Override 30 | public void collect(int doc) throws IOException { 31 | log.info("{}", doc); 32 | } 33 | }); 34 | } 35 | SimpleCollector d; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/code/FileTraveler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.code; 17 | 18 | import com.gitee.kooder.models.SourceFile; 19 | 20 | /** 21 | * 文件遍历回调接口 22 | * @author Winter Lau 23 | */ 24 | public interface FileTraveler { 25 | 26 | /** 27 | * 更新源码文档(新文件、更改文件) 28 | * @param doc 文档信息 29 | * @return true: 继续下一个文档, false 不再处理下面文档 30 | */ 31 | void updateDocument(SourceFile doc); 32 | 33 | /** 34 | * 删除文档 35 | * @param doc 36 | * @return 37 | */ 38 | void deleteDocument(SourceFile doc); 39 | 40 | /** 41 | * 清空仓库所有文件,以待重建 42 | * @param repoId 43 | */ 44 | void resetRepository(long repoId); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/query/QueryFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.query; 17 | 18 | /** 19 | * 查询工具包 20 | * @author Winter Lau 21 | */ 22 | public class QueryFactory { 23 | 24 | /** 25 | * 仓库查询器 26 | * @return 27 | */ 28 | public final static RepoQuery REPO() { 29 | return new RepoQuery(); 30 | } 31 | 32 | /** 33 | * Issue 任务查询器 34 | * @return 35 | */ 36 | public final static IssueQuery ISSUE() { 37 | return new IssueQuery(); 38 | } 39 | 40 | /** 41 | * 源码查询器 42 | * @return 43 | */ 44 | public final static CodeQuery CODE() { 45 | return new CodeQuery(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitee/GiteeWebHookEvent.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.gitee; 17 | 18 | import java.util.Arrays; 19 | 20 | /** 21 | * @author zhanggx 22 | */ 23 | public enum GiteeWebHookEvent { 24 | 25 | REPO_HOOK("Repo Hook"), 26 | PUSH_HOOK("Push Hook"), 27 | ISSUE_HOOK("Issue Hook"); 28 | 29 | private String event; 30 | 31 | GiteeWebHookEvent(String event) { 32 | this.event = event; 33 | } 34 | 35 | public static GiteeWebHookEvent getEvent(String event) { 36 | return Arrays.stream(values()) 37 | .filter(giteeWebHookEvent -> giteeWebHookEvent.event.equals(event)) 38 | .findFirst() 39 | .orElse(null); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitee/Enterprise.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.gitee; 17 | 18 | /** 19 | * @author zhanggx 20 | */ 21 | public class Enterprise { 22 | 23 | private Integer id; 24 | private String name; 25 | private String url; 26 | 27 | public Integer getId() { 28 | return id; 29 | } 30 | 31 | public void setId(Integer id) { 32 | this.id = id; 33 | } 34 | 35 | public String getName() { 36 | return name; 37 | } 38 | 39 | public void setName(String name) { 40 | this.name = name; 41 | } 42 | 43 | public String getUrl() { 44 | return url; 45 | } 46 | 47 | public void setUrl(String url) { 48 | this.url = url; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitea/RepositoryWebHook.java: -------------------------------------------------------------------------------- 1 | package com.gitee.kooder.gitea; 2 | 3 | /** 4 | * @author zhanggx 5 | */ 6 | public class RepositoryWebHook { 7 | 8 | public static final String ACTION_CREATED = "created"; 9 | public static final String ACTION_DELETED = "deleted"; 10 | 11 | private String secret; 12 | private String action; 13 | private Repository repository; 14 | private User organization; 15 | private User sender; 16 | 17 | public String getSecret() { 18 | return secret; 19 | } 20 | 21 | public void setSecret(String secret) { 22 | this.secret = secret; 23 | } 24 | 25 | public String getAction() { 26 | return action; 27 | } 28 | 29 | public void setAction(String action) { 30 | this.action = action; 31 | } 32 | 33 | public Repository getRepository() { 34 | return repository; 35 | } 36 | 37 | public void setRepository(Repository repository) { 38 | this.repository = repository; 39 | } 40 | 41 | public User getOrganization() { 42 | return organization; 43 | } 44 | 45 | public void setOrganization(User organization) { 46 | this.organization = organization; 47 | } 48 | 49 | public User getSender() { 50 | return sender; 51 | } 52 | 53 | public void setSender(User sender) { 54 | this.sender = sender; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitee/User.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.gitee; 17 | 18 | /** 19 | * @author zhanggx 20 | */ 21 | public class User { 22 | 23 | private Integer id; 24 | 25 | private String name; 26 | 27 | private String htmlUrl; 28 | 29 | public Integer getId() { 30 | return id; 31 | } 32 | 33 | public void setId(Integer id) { 34 | this.id = id; 35 | } 36 | 37 | public String getName() { 38 | return name; 39 | } 40 | 41 | public void setName(String name) { 42 | this.name = name; 43 | } 44 | 45 | public String getHtmlUrl() { 46 | return htmlUrl; 47 | } 48 | 49 | public void setHtmlUrl(String htmlUrl) { 50 | this.htmlUrl = htmlUrl; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitea/IssueWebHook.java: -------------------------------------------------------------------------------- 1 | package com.gitee.kooder.gitea; 2 | 3 | /** 4 | * @author zhanggx 5 | */ 6 | public class IssueWebHook { 7 | 8 | private String secret; 9 | private String action; 10 | private Integer number; 11 | private Issue issue; 12 | private Repository repository; 13 | private User sender; 14 | 15 | public String getSecret() { 16 | return secret; 17 | } 18 | 19 | public void setSecret(String secret) { 20 | this.secret = secret; 21 | } 22 | 23 | public String getAction() { 24 | return action; 25 | } 26 | 27 | public void setAction(String action) { 28 | this.action = action; 29 | } 30 | 31 | public Integer getNumber() { 32 | return number; 33 | } 34 | 35 | public void setNumber(Integer number) { 36 | this.number = number; 37 | } 38 | 39 | public Issue getIssue() { 40 | return issue; 41 | } 42 | 43 | public void setIssue(Issue issue) { 44 | this.issue = issue; 45 | } 46 | 47 | public Repository getRepository() { 48 | return repository; 49 | } 50 | 51 | public void setRepository(Repository repository) { 52 | this.repository = repository; 53 | } 54 | 55 | public User getSender() { 56 | return sender; 57 | } 58 | 59 | public void setSender(User sender) { 60 | this.sender = sender; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitee/RepoWebHook.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.gitee; 17 | 18 | /** 19 | * @author zhanggx 20 | */ 21 | public class RepoWebHook { 22 | 23 | public static final String ACTION_CREATE = "create"; 24 | public static final String ACTION_DESTROY = "destroy"; 25 | 26 | private String action; 27 | 28 | private Repository repository; 29 | 30 | public String getAction() { 31 | return action; 32 | } 33 | 34 | public void setAction(String action) { 35 | this.action = action; 36 | } 37 | 38 | public Repository getRepository() { 39 | return repository; 40 | } 41 | 42 | public void setRepository(Repository repository) { 43 | this.repository = repository; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/code/SvnRepositoryProvider.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.code; 17 | 18 | import com.gitee.kooder.models.CodeRepository; 19 | 20 | /** 21 | * TODO: SVN 仓库源 22 | * @author Winter Lau 23 | */ 24 | public class SvnRepositoryProvider implements RepositoryProvider { 25 | 26 | @Override 27 | public String name() { 28 | return "svn"; 29 | } 30 | 31 | /** 32 | * 更新仓库 33 | * @param repo 34 | * @param traveler 35 | * @return 36 | */ 37 | @Override 38 | public int pull(CodeRepository repo, FileTraveler traveler) { 39 | return -1; 40 | } 41 | 42 | /** 43 | * 删除仓库 44 | * @param repo 45 | */ 46 | @Override 47 | public void delete(CodeRepository repo) { 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/code/FileRepositoryProvider.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.code; 17 | 18 | import com.gitee.kooder.models.CodeRepository; 19 | 20 | /** 21 | * TODO: 基于本地文件的仓库源 22 | * @author Winter Lau 23 | */ 24 | public class FileRepositoryProvider implements RepositoryProvider { 25 | 26 | @Override 27 | public String name() { 28 | return "file"; 29 | } 30 | 31 | /** 32 | * 更新仓库 33 | * @param repo 34 | * @param traveler 35 | * @return 36 | */ 37 | @Override 38 | public int pull(CodeRepository repo, FileTraveler traveler) { 39 | return -1; 40 | } 41 | 42 | /** 43 | * 删除仓库 44 | * @param repo 45 | */ 46 | @Override 47 | public void delete(CodeRepository repo) { 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/models/CodeLine.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.models; 17 | 18 | /** 19 | * Code Line 20 | * used to show results of code search 21 | * @author Winter Lau 22 | */ 23 | public class CodeLine { 24 | 25 | private int line; //Line num 26 | private String code; //Code 27 | 28 | public CodeLine(int line, String code) { 29 | this.code = code; 30 | this.line = line; 31 | } 32 | 33 | public int getLine() { 34 | return line; 35 | } 36 | 37 | public void setLine(int line) { 38 | this.line = line; 39 | } 40 | 41 | public StringBuffer getCode() { 42 | return new StringBuffer(code); 43 | } 44 | 45 | public void setCode(String code) { 46 | this.code = code; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/jcseg/JcsegAnalyzer.java: -------------------------------------------------------------------------------- 1 | package com.gitee.kooder.jcseg; 2 | 3 | import org.apache.lucene.analysis.Analyzer; 4 | import org.apache.lucene.analysis.Tokenizer; 5 | import org.lionsoul.jcseg.ISegment; 6 | import org.lionsoul.jcseg.dic.ADictionary; 7 | import org.lionsoul.jcseg.segmenter.SegmenterConfig; 8 | 9 | /** 10 | * Jcseg analyzer for lucene with version on or after 5.0 11 | * 12 | * @author chenxin 13 | */ 14 | public final class JcsegAnalyzer extends Analyzer 15 | { 16 | public ISegment.Type type; 17 | public final SegmenterConfig config; 18 | public final ADictionary dic; 19 | 20 | /** 21 | * initialize the analyzer with the specified mode, configuration, dictionary 22 | * 23 | * @param type 24 | * @param config 25 | * @param dic 26 | */ 27 | public JcsegAnalyzer(ISegment.Type type, SegmenterConfig config, ADictionary dic) 28 | { 29 | this.type = type; 30 | this.config = config; 31 | this.dic = dic; 32 | } 33 | 34 | public SegmenterConfig getConfig() 35 | { 36 | return config; 37 | } 38 | 39 | public ADictionary getDict() 40 | { 41 | return dic; 42 | } 43 | 44 | @Override 45 | protected TokenStreamComponents createComponents(String fieldName) 46 | { 47 | final Tokenizer tokenizer = new JcsegTokenizer(type, config, dic); 48 | return new TokenStreamComponents(tokenizer); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/gitee/kooder/action/IndexAction.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.action; 17 | 18 | import com.gitee.kooder.query.QueryFactory; 19 | import com.gitee.kooder.server.Action; 20 | import io.vertx.ext.web.RoutingContext; 21 | 22 | import java.io.IOException; 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | 26 | /** 27 | * Default action for web 28 | * @author Winter Lau 29 | */ 30 | public class IndexAction implements Action { 31 | 32 | /** 33 | * web searcher 34 | * @param context 35 | * @return 36 | */ 37 | public void index(RoutingContext context) throws IOException { 38 | Map params = new HashMap(); 39 | params.put("total_repo_count", QueryFactory.REPO().totalCount()); 40 | this.vm(context, "index.vm", params); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/queue/QueueProvider.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.queue; 17 | 18 | import com.gitee.kooder.core.Constants; 19 | 20 | import java.util.Arrays; 21 | import java.util.List; 22 | 23 | /** 24 | * 定义了获取索引任务的队列接口 25 | * @author Winter Lau 26 | */ 27 | public interface QueueProvider extends AutoCloseable { 28 | 29 | List TYPES = Arrays.asList(Constants.TYPE_REPOSITORY, Constants.TYPE_ISSUE, Constants.TYPE_CODE); 30 | 31 | /** 32 | * Provider 唯一标识 33 | * @return 34 | */ 35 | String name(); 36 | 37 | /** 38 | * 获取支持的所有任务类型 39 | * @return 40 | */ 41 | default List getAllTypes() { 42 | return TYPES; 43 | } 44 | 45 | /** 46 | * 获取某个任务类型的队列 47 | * @param type 48 | * @return 49 | */ 50 | Queue queue(String type); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /kooder.properties: -------------------------------------------------------------------------------- 1 | # Gitee Search 2 | 3 | # Gitee Search Gateway configurations 4 | http.url = http://192.168.1.25:8080 5 | http.bind = 6 | http.port = 8080 7 | http.log = on 8 | http.webroot = gateway/src/main/webapp 9 | http.startup.tasks = indexer,gitlab 10 | 11 | file.index.path = d://file.txt 12 | file.index.vender = 13 | 14 | # gitlab setting 15 | gitlab.url = http://192.168.1.25:10080/ 16 | gitlab.personal_access_token = Z66e7sxoH18twrkyYzoG 17 | gitlab.secret_token = gsearch 18 | gitlab.connect_timeout = 2000 19 | gitlab.read_timeout = 10000 20 | 21 | # gitee setting 22 | gitee.url = http://giteehost/ 23 | gitee.personal_access_token = bb319595dc98bb8fbdcf3fc442c25893 24 | 25 | # Git 26 | git.username = admin@test.com 27 | git.password = bb319595dc98bb8fbdcf3fc442c25893 28 | # git.ssh.key = ./data/ssh_key 29 | # git.ssh.keypass = 30 | 31 | # 32 | queue.provider = embed 33 | queue.redis.host = 127.0.0.1 34 | queue.redis.port = 6379 35 | queue.redis.database = 1 36 | queue.redis.key = gsearch-queue 37 | 38 | # queue.embed.url = http://127.0.0.1:8080/queue/fetch 39 | queue.embed.path = ./data/queue 40 | queue.embed.batch_size = 10000 41 | 42 | # 43 | storage.type = disk 44 | storage.disk.path = ./data/lucene 45 | storage.disk.use_compound_file = false 46 | storage.disk.max_buffered_docs = -1 47 | storage.disk.ram_buffer_size_mb = 16 48 | 49 | # 50 | storage.repositories.path = ./data/repositories 51 | storage.repositories.max_size_in_gigabyte = 200 52 | 53 | 54 | indexer.no_task_interval = 1000 55 | indexer.batch_fetch_count = 10 56 | indexer.tasks_per_thread = 2 57 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/queue/QueueFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.queue; 17 | 18 | import com.gitee.kooder.core.KooderConfig; 19 | import org.apache.commons.lang3.StringUtils; 20 | 21 | import java.util.Properties; 22 | 23 | /** 24 | * 队列工厂 25 | * @author Winter Lau 26 | */ 27 | public class QueueFactory { 28 | 29 | static QueueProvider provider; 30 | 31 | static { 32 | Properties props = KooderConfig.getQueueProperties(); 33 | String type = StringUtils.trim(props.getProperty("provider")); 34 | if("redis".equalsIgnoreCase(type)) 35 | provider = new RedisQueueProvider(props); 36 | else if("embed".equalsIgnoreCase(type)) 37 | provider = new EmbedQueueProvider(props); 38 | } 39 | 40 | public final static QueueProvider getProvider() { 41 | return provider; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/gitee/kooder/action/GiteaAction.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.action; 17 | 18 | import com.gitee.kooder.core.Constants; 19 | import com.gitee.kooder.core.KooderConfig; 20 | import com.gitee.kooder.gitea.GiteaException; 21 | import com.gitee.kooder.server.Action; 22 | import com.gitee.kooder.webhook.GiteaSystemHookManager; 23 | import io.vertx.ext.web.RoutingContext; 24 | 25 | /** 26 | * Handle Gitea webhook 27 | * http://localhost:8080/gitea web hook (new project/project updated etc) 28 | * @author Winter Lau 29 | */ 30 | public class GiteaAction implements Action { 31 | 32 | String SECRET_TOKEN = KooderConfig.getProperty("gitea.secret_token", Constants.DEFAULT_SECRET_TOKEN); 33 | 34 | /** 35 | * Gitea webhook handler 36 | * @param context 37 | */ 38 | public void index(RoutingContext context) throws GiteaException { 39 | GiteaSystemHookManager.handleEvent(SECRET_TOKEN, context); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/gitee/kooder/action/GiteeAction.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.action; 17 | 18 | import com.gitee.kooder.core.Constants; 19 | import com.gitee.kooder.core.KooderConfig; 20 | import com.gitee.kooder.gitee.GiteeException; 21 | import com.gitee.kooder.server.Action; 22 | import com.gitee.kooder.webhook.GiteeWebHookManager; 23 | import io.vertx.ext.web.RoutingContext; 24 | 25 | /** 26 | * Handle Gitee webhook 27 | * http://localhost:8080/gitee web hook (new project/project updated etc) 28 | * 29 | * @author Winter Lau 30 | */ 31 | public class GiteeAction implements Action { 32 | 33 | String SECRET_TOKEN = KooderConfig.getProperty("gitee.secret_token", Constants.DEFAULT_SECRET_TOKEN); 34 | 35 | /** 36 | * Gitee webhook handler 37 | * 38 | * @param context 39 | */ 40 | public void index(RoutingContext context) throws GiteeException { 41 | GiteeWebHookManager.handleEvent(SECRET_TOKEN, context); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/code/RepositoryFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.code; 17 | 18 | import com.gitee.kooder.models.CodeRepository; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | import java.util.*; 23 | 24 | /** 25 | * 各种仓库源的管理 26 | * @author Winter Lau 27 | */ 28 | public class RepositoryFactory { 29 | 30 | private final static Logger log = LoggerFactory.getLogger(RepositoryFactory.class); 31 | 32 | private final static Map providers = new HashMap(){{ 33 | put(CodeRepository.SCM_GIT, new GitRepositoryProvider()); 34 | put(CodeRepository.SCM_SVN, new SvnRepositoryProvider()); 35 | put(CodeRepository.SCM_FILE, new FileRepositoryProvider()); 36 | }}; 37 | 38 | /** 39 | * 根据 scm 获取仓库操作类实例 40 | * @param scm 41 | * @return 42 | */ 43 | public final static RepositoryProvider getProvider(String scm) { 44 | return providers.get(scm); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/utils/FileClassifierResult.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.utils; 17 | 18 | /** 19 | * This class used to mapping language.json segment defined 20 | * @author Winter Lau 21 | */ 22 | public class FileClassifierResult { 23 | 24 | public String[] extensions; 25 | public String[] extensionfile; 26 | public String[] line_comment; 27 | public String[] complexitychecks; 28 | public String[][] multi_line; 29 | public LanguageQuote[] quotes; 30 | public boolean nestedmultiline; 31 | public String[] keywords; // Used to identify languages that share extensions 32 | public String[] filenames; 33 | public String comment; 34 | 35 | public FileClassifierResult(){} 36 | 37 | public FileClassifierResult(String extensions) { 38 | this.extensions = extensions.toLowerCase().split(","); 39 | } 40 | 41 | public FileClassifierResult(String extensions, String keywords) { 42 | this.extensions = extensions.toLowerCase().split(","); 43 | this.keywords = keywords.toLowerCase().split(","); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | # SLF4J's SimpleLogger configuration file 2 | # Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. 3 | # org.slf4j.simpleLogger.logFile=System.out 4 | 5 | # Default logging detail level for all instances of SimpleLogger. 6 | # Must be one of ("trace", "debug", "info", "warn", or "error"). 7 | # If not specified, defaults to "info". 8 | org.slf4j.simpleLogger.defaultLogLevel=info 9 | 10 | # Logging detail level for a SimpleLogger instance named "xxxxx". 11 | # Must be one of ("trace", "debug", "info", "warn", or "error"). 12 | # If not specified, the default logging detail level is used. 13 | #org.slf4j.simpleLogger.log.xxxxx= 14 | 15 | # Set to true if you want the current date and time to be included in output messages. 16 | # Default is false, and will output the number of milliseconds elapsed since startup. 17 | org.slf4j.simpleLogger.showDateTime=true 18 | 19 | # The date and time format to be used in the output messages. 20 | # The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat. 21 | # If the format is not specified or is invalid, the default format is used. 22 | # The default format is yyyy-MM-dd HH:mm:ss:SSS Z. 23 | org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss 24 | 25 | # Set to true if you want to output the current thread name. 26 | # Defaults to true. 27 | org.slf4j.simpleLogger.showThreadName=false 28 | 29 | # Set to true if you want the Logger instance name to be included in output messages. 30 | # Defaults to true. 31 | org.slf4j.simpleLogger.showLogName=true 32 | 33 | # Set to true if you want the last component of the name to be included in output messages. 34 | # Defaults to false. 35 | org.slf4j.simpleLogger.showShortLogName=true -------------------------------------------------------------------------------- /gateway/src/main/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | # SLF4J's SimpleLogger configuration file 2 | # Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. 3 | #org.slf4j.simpleLogger.logFile=System.out 4 | 5 | # Default logging detail level for all instances of SimpleLogger. 6 | # Must be one of ("trace", "debug", "info", "warn", or "error"). 7 | # If not specified, defaults to "info". 8 | org.slf4j.simpleLogger.defaultLogLevel=info 9 | 10 | # Logging detail level for a SimpleLogger instance named "xxxxx". 11 | # Must be one of ("trace", "debug", "info", "warn", or "error"). 12 | # If not specified, the default logging detail level is used. 13 | #org.slf4j.simpleLogger.log.xxxxx= 14 | 15 | # Set to true if you want the current date and time to be included in output messages. 16 | # Default is false, and will output the number of milliseconds elapsed since startup. 17 | org.slf4j.simpleLogger.showDateTime=true 18 | 19 | # The date and time format to be used in the output messages. 20 | # The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat. 21 | # If the format is not specified or is invalid, the default format is used. 22 | # The default format is yyyy-MM-dd HH:mm:ss:SSS Z. 23 | org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss 24 | 25 | # Set to true if you want to output the current thread name. 26 | # Defaults to true. 27 | org.slf4j.simpleLogger.showThreadName=false 28 | 29 | # Set to true if you want the Logger instance name to be included in output messages. 30 | # Defaults to true. 31 | org.slf4j.simpleLogger.showLogName=true 32 | 33 | # Set to true if you want the last component of the name to be included in output messages. 34 | # Defaults to false. 35 | org.slf4j.simpleLogger.showShortLogName=true -------------------------------------------------------------------------------- /indexer/src/main/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | # SLF4J's SimpleLogger configuration file 2 | # Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. 3 | # org.slf4j.simpleLogger.logFile=System.out 4 | 5 | # Default logging detail level for all instances of SimpleLogger. 6 | # Must be one of ("trace", "debug", "info", "warn", or "error"). 7 | # If not specified, defaults to "info". 8 | org.slf4j.simpleLogger.defaultLogLevel=info 9 | 10 | # Logging detail level for a SimpleLogger instance named "xxxxx". 11 | # Must be one of ("trace", "debug", "info", "warn", or "error"). 12 | # If not specified, the default logging detail level is used. 13 | #org.slf4j.simpleLogger.log.xxxxx= 14 | 15 | # Set to true if you want the current date and time to be included in output messages. 16 | # Default is false, and will output the number of milliseconds elapsed since startup. 17 | org.slf4j.simpleLogger.showDateTime=true 18 | 19 | # The date and time format to be used in the output messages. 20 | # The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat. 21 | # If the format is not specified or is invalid, the default format is used. 22 | # The default format is yyyy-MM-dd HH:mm:ss:SSS Z. 23 | org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss 24 | 25 | # Set to true if you want to output the current thread name. 26 | # Defaults to true. 27 | org.slf4j.simpleLogger.showThreadName=false 28 | 29 | # Set to true if you want the Logger instance name to be included in output messages. 30 | # Defaults to true. 31 | org.slf4j.simpleLogger.showLogName=true 32 | 33 | # Set to true if you want the last component of the name to be included in output messages. 34 | # Defaults to false. 35 | org.slf4j.simpleLogger.showShortLogName=true -------------------------------------------------------------------------------- /gateway/src/main/webapp/css/main.css: -------------------------------------------------------------------------------- 1 | 2 | body {padding:8px;font-size:10pt;} 3 | 4 | em.highlight {color:red;font-style:normal;} 5 | em.md {font-style:normal;} 6 | em.codeline {width:24px;text-align:right;font-style:normal;margin-right:10px;} 7 | 8 | #INPUT_SEARCH {width:610px;} 9 | 10 | #SearchResults {float:left;width:740px;margin-left:10px;} 11 | #SearchResults ul { list-style-type:none; margin:20px 0 0 0; padding:0;} 12 | #SearchFacets {float:left; width:200px; } 13 | #SearchResults .facets {margin-top:20px;} 14 | #SearchResults blockquote {padding-top:0;padding-bottom:0;margin:10px 0 5px 0;} 15 | #SearchResults #SortMenu {float:right;margin-right:10px;} 16 | #SortMenu .active { font-weight: bold; } 17 | #SearchResults #SearchTabs {margin:0 20px 20px 0;} 18 | #SearchResults #SearchTabs li {margin-right:20px;} 19 | #SearchResults .breadcrumb {margin:0;} 20 | #SearchResults li.repo {margin-bottom:20px;} 21 | #SearchResults li .meta {color:#999;} 22 | #SearchResults li .meta a {color:#999;} 23 | #SearchResults li .meta .label {margin-right:10px;} 24 | 25 | blockquote.CodeResults ol {list-style-type:none;margin:0;padding:0} 26 | blockquote.CodeResults ol li {margin:0;padding:0} 27 | blockquote.CodeResults ol li:after {content: ""; display: table; clear: both;} 28 | blockquote.CodeResults ol li em.line {font-style:normal;float:left; width: 26px; padding-right:3px; text-align:right;background:#f2f2f2;} 29 | blockquote.CodeResults ol li em.line a {color: black;} 30 | blockquote.CodeResults ol li em.line a:hover {color: #5755d9;} 31 | blockquote.CodeResults ol li em.code {float:left;width:590px; margin-left:6px;font-style:normal;word-wrap:break-word;} 32 | 33 | #SearchFacets .menu {margin-bottom:20px;} 34 | #SearchFacets .menu .active {font-weight:bold;} 35 | #SearchFacets li a {overflow: hidden;text-overflow:ellipsis;white-space: nowrap;} -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitee/Label.java: -------------------------------------------------------------------------------- 1 | package com.gitee.kooder.gitee; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | 5 | import java.util.Date; 6 | 7 | /** 8 | * @author zhanggx 9 | */ 10 | public class Label { 11 | 12 | private Integer id; 13 | private String name; 14 | private String color; 15 | private Integer repositoryId; 16 | private String url; 17 | @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssX") 18 | private Date createdAt; 19 | @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssX") 20 | private Date updatedAt; 21 | 22 | public Integer getId() { 23 | return id; 24 | } 25 | 26 | public void setId(Integer id) { 27 | this.id = id; 28 | } 29 | 30 | public String getName() { 31 | return name; 32 | } 33 | 34 | public void setName(String name) { 35 | this.name = name; 36 | } 37 | 38 | public String getColor() { 39 | return color; 40 | } 41 | 42 | public void setColor(String color) { 43 | this.color = color; 44 | } 45 | 46 | public Integer getRepositoryId() { 47 | return repositoryId; 48 | } 49 | 50 | public void setRepositoryId(Integer repositoryId) { 51 | this.repositoryId = repositoryId; 52 | } 53 | 54 | public String getUrl() { 55 | return url; 56 | } 57 | 58 | public void setUrl(String url) { 59 | this.url = url; 60 | } 61 | 62 | public Date getCreatedAt() { 63 | return createdAt; 64 | } 65 | 66 | public void setCreatedAt(Date createdAt) { 67 | this.createdAt = createdAt; 68 | } 69 | 70 | public Date getUpdatedAt() { 71 | return updatedAt; 72 | } 73 | 74 | public void setUpdatedAt(Date updatedAt) { 75 | this.updatedAt = updatedAt; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitea/PushWebHook.java: -------------------------------------------------------------------------------- 1 | package com.gitee.kooder.gitea; 2 | 3 | /** 4 | * @author zhanggx 5 | */ 6 | public class PushWebHook { 7 | 8 | private String secret; 9 | private String ref; 10 | private String before; 11 | private String after; 12 | private String compareUrl; 13 | private Repository repository; 14 | private User pusher; 15 | private User sender; 16 | 17 | public String getSecret() { 18 | return secret; 19 | } 20 | 21 | public void setSecret(String secret) { 22 | this.secret = secret; 23 | } 24 | 25 | public String getRef() { 26 | return ref; 27 | } 28 | 29 | public void setRef(String ref) { 30 | this.ref = ref; 31 | } 32 | 33 | public String getBefore() { 34 | return before; 35 | } 36 | 37 | public void setBefore(String before) { 38 | this.before = before; 39 | } 40 | 41 | public String getAfter() { 42 | return after; 43 | } 44 | 45 | public void setAfter(String after) { 46 | this.after = after; 47 | } 48 | 49 | public String getCompareUrl() { 50 | return compareUrl; 51 | } 52 | 53 | public void setCompareUrl(String compareUrl) { 54 | this.compareUrl = compareUrl; 55 | } 56 | 57 | public Repository getRepository() { 58 | return repository; 59 | } 60 | 61 | public void setRepository(Repository repository) { 62 | this.repository = repository; 63 | } 64 | 65 | public User getPusher() { 66 | return pusher; 67 | } 68 | 69 | public void setPusher(User pusher) { 70 | this.pusher = pusher; 71 | } 72 | 73 | public User getSender() { 74 | return sender; 75 | } 76 | 77 | public void setSender(User sender) { 78 | this.sender = sender; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /bin/service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # description: jsw-test 3 | # processname: jsw-test 4 | # chkconfig: 234 20 80 5 | 6 | # 程序名称 7 | SERVICE_NAME=jsw-test 8 | #程序路径获取相对路径,可填写绝对路径,如APP_HOME=/usr/local/bingo_cdp/deploy/bingo-cdp-timerjob-linux 9 | APP_HOME=$(dirname $(pwd)) 10 | EXEC=/opt/jsvc/commons-daemon-1.0.15-src/src/native/unix/jsvc 11 | JAVA_HOME=/usr/java/jdk1.8.0_51 12 | 13 | #依赖路径 14 | cd ${APP_HOME} 15 | CLASS_PATH="$PWD/classes":"$PWD/lib/*" 16 | 17 | #程序入口类 18 | CLASS=main.DaemonMainClassForLinux 19 | 20 | #程序ID文件 21 | PID=${APP_HOME}/${SERVICE_NAME}.pid 22 | #日志输出路径 23 | LOG_OUT=${APP_HOME}/logs/${SERVICE_NAME}.out 24 | LOG_ERR=${APP_HOME}/logs/${SERVICE_NAME}.err 25 | 26 | #输出 27 | echo "service name: $SERVICE_NAME" 28 | echo "app home: $APP_HOME" 29 | echo "jsvc: $EXEC" 30 | echo "java home: $JAVA_HOME" 31 | echo "class path: $CLASS_PATH" 32 | echo "main class: $CLASS" 33 | 34 | #执行 35 | do_exec() 36 | { 37 | $EXEC -home "$JAVA_HOME" -Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8 -Djcifs.smb.client.dfs.disabled=false -Djcifs.resolveOrder=DNS -Xms512M -Xmx1024M -cp $CLASS_PATH -outfile $LOG_OUT -errfile $LOG_ERR -pidfile $PID $1 $CLASS 38 | } 39 | 40 | #根据参数执行 41 | case "$1" in 42 | start) 43 | do_exec 44 | echo "${SERVICE_NAME} started" 45 | ;; 46 | stop) 47 | do_exec "-stop" 48 | echo "${SERVICE_NAME} stopped" 49 | ;; 50 | restart) 51 | if [ -f "$PID" ]; then 52 | do_exec "-stop" 53 | do_exec 54 | echo "${SERVICE_NAME} restarted" 55 | else 56 | echo "service not running, will do nothing" 57 | exit 1 58 | fi 59 | ;; 60 | status) 61 | ps -ef | grep jsvc 62 | ;; 63 | *) 64 | echo "usage: service ${SERVICE_NAME} {start|stop|restart|status}" >&2 65 | exit 3 66 | ;; 67 | esac -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/models/Relation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.models; 17 | 18 | /** 19 | * Relation info 20 | * @author Winter Lau 21 | */ 22 | public final class Relation { 23 | 24 | protected long id; 25 | protected String name; 26 | protected String url; 27 | 28 | public Relation() {} 29 | 30 | public Relation(long id, String name, String url) { 31 | this.id = id; 32 | this.name = name; 33 | this.url = url; 34 | } 35 | 36 | public final static Relation EMPTY() { 37 | return new Relation(0, "NONE", null); 38 | } 39 | 40 | public long getId() { 41 | return id; 42 | } 43 | 44 | public void setId(long id) { 45 | this.id = id; 46 | } 47 | 48 | public String getName() { 49 | return name; 50 | } 51 | 52 | public void setName(String name) { 53 | this.name = name; 54 | } 55 | 56 | public String getUrl() { 57 | return url; 58 | } 59 | 60 | public void setUrl(String url) { 61 | this.url = url; 62 | } 63 | 64 | @Override 65 | public boolean equals(Object obj) { 66 | if(obj == null) 67 | return false; 68 | if(!(obj instanceof Relation)) 69 | return false; 70 | return id == ((Relation)obj).id; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/gitee/kooder/server/Tester.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.server; 17 | 18 | import org.apache.commons.lang3.StringUtils; 19 | import org.jsoup.Jsoup; 20 | import org.jsoup.nodes.Document; 21 | import org.jsoup.select.Elements; 22 | 23 | import java.io.IOException; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | public class Tester { 28 | 29 | public static void main(String[] args) throws IOException { 30 | List repos = new ArrayList<>(); 31 | for(int p = 1; p <= 4; p++) { 32 | Document doc = Jsoup.connect("https://github.com/EdgexFoundry?page=" + p).get(); 33 | Elements elems = doc.select("a[itemprop]"); 34 | elems.forEach( e -> { 35 | String url = e.attr("href"); 36 | if(url.startsWith("/edgexfoundry")) { 37 | //System.out.println(url); 38 | repos.add(url); 39 | } 40 | }); 41 | } 42 | 43 | repos.forEach(repo -> { 44 | String url = "https://gitee.com" + StringUtils.replace(repo, "edgexfoundry", "EdgexFoundry"); 45 | try { 46 | Jsoup.connect(url).get(); 47 | } catch (IOException e) { 48 | System.out.println(repo); 49 | } 50 | //System.out.println(url); 51 | }); 52 | 53 | System.out.println(repos.size()); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /docs/configuration.md: -------------------------------------------------------------------------------- 1 | ## Kooder 配置 2 | 3 | Kooder 配置文件位于源码中的 `core/src/main/resource/kooder.properties` 文件中。 4 | 每次修改完该文件需要重新 `maven install` 重新打包,并重启服务。 5 | 6 | ### [ kooder.properties ] 7 | 8 | Web Service configurations 9 | 10 | `http.bind = 127.0.0.1` HTTP 绑定的 IP 地址,默认是 127.0.0.1 11 | `http.port = 8080` HTTP 服务端口 12 | `http.log.pattern = /,/index/*,/search/*,/api/*` 记录访问日志的请求前缀 13 | `http.webroot = gateway/src/main/webapp` Web 静态文件和模板文件的存放目录 14 | `http.startup.tasks = indexer` 将 `indexer` 依附到 `gateway` 进程中运行 15 | 16 | [Gitlab configurations] 17 | 18 | `gitlab.url = ` Gitlab 服务地址 19 | `gitlab.personal_access_token = xxx` Gitlab 管理员账号的 access token 20 | `gitlab.secret_token = gsearch` Webhook 回调的密钥 21 | `gitlab.connect_timeout = 2000` 连接超时设置,单位毫秒 22 | `gitlab.read_timeout = 10000` 数据读取超时设置,单位毫秒 23 | 24 | Git access configrations 25 | 26 | `git.username = xxx` 访问 git 仓库的用户名 27 | `git.password = xxx` 访问 Git 仓库的密码 28 | `# git.ssh.key = ./data/ssh_key` 使用 SSH 方式访问 Git 仓库的密钥 29 | `# git.ssh.keypass = xxx` SSH 密钥对应的密码 30 | 31 | 持久化任务队列配置,支持两种队列 redis 和 embed 。 32 | 其中 embed 队列是内建队列,使用磁盘存储队列中信息。 。 33 | 34 | `queue.provider = embed` using embed queue 35 | `queue.types = repo,issue,code` 36 | `queue.redis.host = 127.0.0.1` Redis service host 37 | `queue.redis.port = 6379` Redis service port 38 | `queue.redis.database = 1` Redis service database 39 | `queue.redis.key = gsearch-queue` Redis queue key 40 | 41 | `queue.embed.path = ./data/queue` embed queue storage path 42 | `queue.embed.batch_size = 10000` batch queue size for embed 43 | 44 | Lucene storage configurations 45 | 46 | `storage.type = disk` 47 | `storage.disk.path = ./data/lucene` 48 | `storage.disk.use_compound_file = false` 49 | `storage.disk.max_buffered_docs = -1` 50 | `storage.disk.ram_buffer_size_mb = 16` 51 | 52 | git repository storage configurations 53 | 54 | `storage.repositories.path = ./data/repositories` 55 | `storage.repositories.max_size_in_gigabyte = 200` 56 | 57 | Task thread configurations 58 | 59 | `indexer.no_task_interval = 1000` 60 | `indexer.batch_fetch_count = 10` 61 | `indexer.tasks_per_thread = 2` -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/indexer/ServerDaemon.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.indexer; 17 | 18 | import org.apache.commons.daemon.Daemon; 19 | import org.apache.commons.daemon.DaemonContext; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | /** 24 | * Make gateway as a daemon 25 | * @author Winter Lau 26 | */ 27 | public class ServerDaemon implements Daemon { 28 | 29 | private final static Logger log = LoggerFactory.getLogger(ServerDaemon.class); 30 | 31 | private FetchTaskThread fetchTaskThread; 32 | 33 | public ServerDaemon() {} 34 | 35 | /** 36 | * 命令行启动服务 37 | * @param args 38 | */ 39 | public static void main(String[] args) { 40 | ServerDaemon daemon = new ServerDaemon(); 41 | daemon.init(null); 42 | daemon.start(); 43 | log.info("Gitee Search Indexer started !"); 44 | } 45 | 46 | @Override 47 | public void init(DaemonContext dc) { 48 | this.fetchTaskThread = new FetchTaskThread(); 49 | } 50 | 51 | @Override 52 | public void start() { 53 | this.fetchTaskThread.start(); 54 | } 55 | 56 | @Override 57 | public void stop() { 58 | this.fetchTaskThread.interrupt(); 59 | try { 60 | this.fetchTaskThread.join(2000, 20); 61 | } catch (InterruptedException e) {} 62 | } 63 | 64 | @Override 65 | public void destroy() { 66 | log.info("Gitee Search Indexer exit."); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/models/CodeOwner.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.models; 17 | 18 | /** 19 | * The main developer of one source file 20 | * @author Winter Lau 21 | */ 22 | public class CodeOwner { 23 | 24 | private int noLines; 25 | private String name; 26 | private int mostRecentUnixCommitTimestamp; 27 | 28 | public CodeOwner(String name, int noLines, int mostRecentUnixCommitTimestamp) { 29 | this.setName(name); 30 | this.setNoLines(noLines); 31 | this.setMostRecentUnixCommitTimestamp(mostRecentUnixCommitTimestamp); 32 | } 33 | 34 | public void incrementLines() { 35 | this.noLines++; 36 | } 37 | 38 | public int getNoLines() { 39 | return noLines; 40 | } 41 | 42 | public void setNoLines(int noLines) { 43 | this.noLines = noLines; 44 | } 45 | 46 | public String getName() { 47 | return name; 48 | } 49 | 50 | public void setName(String name) { 51 | this.name = name; 52 | } 53 | 54 | public int getMostRecentUnixCommitTimestamp() { 55 | return mostRecentUnixCommitTimestamp; 56 | } 57 | 58 | public void setMostRecentUnixCommitTimestamp(int mostRecentUnixCommitTimestamp) { 59 | this.mostRecentUnixCommitTimestamp = mostRecentUnixCommitTimestamp; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return String.format("Name: %s, Time: %d, LINES: %d", name, mostRecentUnixCommitTimestamp, noLines); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/jcseg/JcsegTokenizerFactory.java: -------------------------------------------------------------------------------- 1 | package com.gitee.kooder.jcseg; 2 | 3 | import java.io.IOException; 4 | import java.util.Map; 5 | import java.util.Map.Entry; 6 | 7 | import org.apache.lucene.analysis.Tokenizer; 8 | import org.apache.lucene.analysis.util.TokenizerFactory; 9 | import org.apache.lucene.util.AttributeFactory; 10 | import org.lionsoul.jcseg.ISegment; 11 | import org.lionsoul.jcseg.dic.ADictionary; 12 | import org.lionsoul.jcseg.dic.DictionaryFactory; 13 | import org.lionsoul.jcseg.segmenter.SegmenterConfig; 14 | 15 | /** 16 | * Jcseg tokenizer factory class for lucene/solr 17 | * 18 | * @author chenxin 19 | */ 20 | public class JcsegTokenizerFactory extends TokenizerFactory 21 | { 22 | 23 | public final ISegment.Type type; 24 | public final SegmenterConfig config; 25 | public final ADictionary dic; 26 | 27 | /** 28 | * set the mode arguments in the schema.xml 29 | * configuration file to change the segment mode for Jcseg 30 | * @throws IOException 31 | * 32 | * @see TokenizerFactory#TokenizerFactory(Map) 33 | */ 34 | public JcsegTokenizerFactory(Map args) throws IOException 35 | { 36 | super(args); 37 | 38 | type = ISegment.Type.fromString(args.get("mode")); 39 | 40 | // initialize the task configuration and the dictionary 41 | config = new SegmenterConfig(true); 42 | // check and apply this-level Jcseg settings 43 | for ( Entry entry : args.entrySet() ) { 44 | if ( entry.getKey().startsWith("jcseg_") ) { 45 | config.set(entry.getKey().replace("jcseg_", "jcseg."), entry.getValue()); 46 | } 47 | } 48 | 49 | dic = DictionaryFactory.createSingletonDictionary(config); 50 | } 51 | 52 | public SegmenterConfig getTaskConfig() 53 | { 54 | return config; 55 | } 56 | 57 | public ADictionary getDict() 58 | { 59 | return dic; 60 | } 61 | 62 | @Override 63 | public Tokenizer create(AttributeFactory factory) 64 | { 65 | return new JcsegTokenizer(type, config, dic); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/gitee/kooder/SearchCmd.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder; 17 | 18 | import com.gitee.kooder.core.Constants; 19 | import com.gitee.kooder.core.SearchHelper; 20 | import com.gitee.kooder.models.QueryResult; 21 | import com.gitee.kooder.query.IQuery; 22 | import com.gitee.kooder.query.QueryFactory; 23 | import jline.TerminalFactory; 24 | import jline.console.ConsoleReader; 25 | 26 | /** 27 | * 测试搜索的控制台程序 28 | * @author Winter Lau 29 | */ 30 | public class SearchCmd { 31 | 32 | static { 33 | jline.TerminalFactory.registerFlavor(TerminalFactory.Flavor.WINDOWS, jline.UnsupportedTerminal.class); 34 | } 35 | 36 | public static void main(String[] args) throws Exception { 37 | ConsoleReader reader = new ConsoleReader(); 38 | do { 39 | String line = reader.readLine("> "); 40 | if (line == null || "quit".equalsIgnoreCase(line) || "exit".equalsIgnoreCase(line)) 41 | break; 42 | long ct = System.currentTimeMillis(); 43 | String q = SearchHelper.cleanupKey(line); 44 | IQuery query = QueryFactory.CODE().setSearchKey(q); 45 | query.addFacets(Constants.FIELD_LANGUAGE, "Java"); 46 | QueryResult result = query.execute(); 47 | System.out.println(result.toString()); 48 | 49 | System.out.println("total time: " + (System.currentTimeMillis() - ct) + " ms"); 50 | 51 | } while(true); 52 | 53 | reader.shutdown(); 54 | System.exit(0); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitea/Organization.java: -------------------------------------------------------------------------------- 1 | package com.gitee.kooder.gitea; 2 | 3 | /** 4 | * @author zhanggx 5 | */ 6 | public class Organization { 7 | 8 | private Integer id; 9 | private String username; 10 | private String fullName; 11 | private String avatarUrl; 12 | private String description; 13 | private String website; 14 | private String location; 15 | private String visibility; 16 | private Boolean repoAdminChangeTeamAccess; 17 | 18 | public Integer getId() { 19 | return id; 20 | } 21 | 22 | public void setId(Integer id) { 23 | this.id = id; 24 | } 25 | 26 | public String getUsername() { 27 | return username; 28 | } 29 | 30 | public void setUsername(String username) { 31 | this.username = username; 32 | } 33 | 34 | public String getFullName() { 35 | return fullName; 36 | } 37 | 38 | public void setFullName(String fullName) { 39 | this.fullName = fullName; 40 | } 41 | 42 | public String getAvatarUrl() { 43 | return avatarUrl; 44 | } 45 | 46 | public void setAvatarUrl(String avatarUrl) { 47 | this.avatarUrl = avatarUrl; 48 | } 49 | 50 | public String getDescription() { 51 | return description; 52 | } 53 | 54 | public void setDescription(String description) { 55 | this.description = description; 56 | } 57 | 58 | public String getWebsite() { 59 | return website; 60 | } 61 | 62 | public void setWebsite(String website) { 63 | this.website = website; 64 | } 65 | 66 | public String getLocation() { 67 | return location; 68 | } 69 | 70 | public void setLocation(String location) { 71 | this.location = location; 72 | } 73 | 74 | public String getVisibility() { 75 | return visibility; 76 | } 77 | 78 | public void setVisibility(String visibility) { 79 | this.visibility = visibility; 80 | } 81 | 82 | public Boolean isRepoAdminChangeTeamAccess() { 83 | return repoAdminChangeTeamAccess; 84 | } 85 | 86 | public void setRepoAdminChangeTeamAccess(Boolean repoAdminChangeTeamAccess) { 87 | this.repoAdminChangeTeamAccess = repoAdminChangeTeamAccess; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/code/TechCodeAnalyzer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.code; 17 | 18 | import org.apache.lucene.analysis.*; 19 | import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; 20 | 21 | import java.io.StringReader; 22 | 23 | /** 24 | * 源码分词器 25 | * @author Winter Lau 26 | */ 27 | public class TechCodeAnalyzer extends Analyzer { 28 | 29 | @Override 30 | protected TokenStreamComponents createComponents(String fieldName) { 31 | return new TokenStreamComponents(new TechCodeTokenizer()); 32 | } 33 | 34 | public static void main(String[] args) throws Exception { 35 | String text = "源码: Hello.java: This is a J2Cache demo of the #CodeAnalyzer .NET API. public void main(String[] args){return 0;} C# C++//我是中国人"; 36 | //String text = FileUtils.readFileToString(new File("D:\\test.txt")); 37 | //String text = "9615 #"; 38 | //String text = "源码: Hello.java China"; 39 | //System.out.println(text); 40 | 41 | Analyzer analyzer = new TechCodeAnalyzer(); 42 | try (TokenStream stream = analyzer.tokenStream(null, new StringReader(text))){ 43 | CharTermAttribute termAtt = stream.addAttribute(CharTermAttribute.class); 44 | stream.reset(); 45 | while (stream.incrementToken()) { 46 | //System.out.print(termAtt.toString() + "\\"); 47 | } 48 | System.out.println(); 49 | stream.end(); 50 | } 51 | //测试高亮 52 | //System.out.println("HLCODE:" + SearchHelper.hlcode(text, "dotnet")); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/gitee/kooder/examples/GroupingSearchExample.java: -------------------------------------------------------------------------------- 1 | package com.gitee.kooder.examples; 2 | 3 | import com.gitee.kooder.storage.StorageFactory; 4 | import org.apache.lucene.document.Document; 5 | import org.apache.lucene.index.IndexReader; 6 | import org.apache.lucene.search.*; 7 | import org.apache.lucene.search.grouping.*; 8 | import org.apache.lucene.util.BytesRef; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.io.IOException; 13 | 14 | /** 15 | * GroupingSearch example 16 | * @author Winter Lau 17 | */ 18 | public class GroupingSearchExample { 19 | 20 | private final static Logger log = LoggerFactory.getLogger(GroupingSearchExample.class); 21 | 22 | public static void main(String[] args) throws IOException { 23 | Sort sort = new Sort(new SortField("count.star", SortField.Type.INT, true)); 24 | GroupingSearch groupingSearch = new GroupingSearch("lang"); 25 | groupingSearch.setGroupSort(sort); 26 | groupingSearch.setSortWithinGroup(sort); 27 | groupingSearch.setGroupDocsLimit(10); 28 | //进行分组的域上建立的必须是SortedDocValuesField类型 29 | try (IndexReader reader = StorageFactory.getIndexReader("repo")) { 30 | IndexSearcher searcher = new IndexSearcher(reader); 31 | long ct = System.currentTimeMillis(); 32 | TopGroups result = groupingSearch.search(searcher, new MatchAllDocsQuery(), 0,10); 33 | 34 | log.info("totalHitCount: {}, groupCount:{}, time:{}ms", result.totalHitCount, result.groups.length, System.currentTimeMillis()-ct); 35 | 36 | // 按照分组打印查询结果 37 | for (GroupDocs groupDocs : result.groups){ 38 | if (groupDocs != null) { 39 | log.info("[{},{}]", groupDocs.groupValue.utf8ToString(), groupDocs.totalHits); 40 | 41 | for(ScoreDoc scoreDoc : groupDocs.scoreDocs){ 42 | Document doc = searcher.doc(scoreDoc.doc); 43 | log.info("\tdoc:{},id:{},name:{},stars:{},score:{}", 44 | scoreDoc.doc, doc.get("id"), doc.get("name"), doc.get("count.star"), scoreDoc.score); 45 | } 46 | } 47 | } 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/indexer/Gitlab.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.indexer; 17 | 18 | import com.gitee.kooder.core.KooderConfig; 19 | import org.apache.commons.lang3.math.NumberUtils; 20 | import org.gitlab4j.api.GitLabApi; 21 | 22 | /** 23 | * Gitlab access instance 24 | * @author Winter Lau 25 | */ 26 | public class Gitlab { 27 | 28 | private static String gitlab_url; 29 | private static String access_token; 30 | private static int version; 31 | private static String gsearch_url; 32 | private static String system_hook_url; 33 | private static String project_hook_url; 34 | private static String secret_token; 35 | 36 | public final static GitLabApi INSTANCE; 37 | 38 | static { 39 | gitlab_url = KooderConfig.getProperty("gitlab.url"); 40 | access_token = KooderConfig.getProperty("gitlab.personal_access_token"); 41 | version = NumberUtils.toInt(KooderConfig.getProperty("gitlab.version"), 4); 42 | gsearch_url = KooderConfig.getProperty("http.url"); 43 | system_hook_url = gsearch_url + "/gitlab/system"; 44 | project_hook_url = gsearch_url + "/gitlab/project"; 45 | secret_token = KooderConfig.getProperty("gitlab.secret_token", "gsearch"); 46 | 47 | INSTANCE = new GitLabApi((version != 3) ? GitLabApi.ApiVersion.V4 : GitLabApi.ApiVersion.V3, gitlab_url, access_token); 48 | // Set the connect timeout to 1 second and the read timeout to 5 seconds 49 | int connectTimeout = NumberUtils.toInt(KooderConfig.getProperty("gitlab.connect_timeout"), 2000); 50 | int readTimeout = NumberUtils.toInt(KooderConfig.getProperty("gitlab.read_timeout"), 10000); 51 | INSTANCE.setRequestTimeout(connectTimeout, readTimeout); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/gitee/kooder/server/AutoContentTypeStaticHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.server; 17 | 18 | import com.gitee.kooder.core.KooderConfig; 19 | import io.vertx.ext.web.RoutingContext; 20 | import io.vertx.ext.web.handler.impl.StaticHandlerImpl; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import java.io.IOException; 25 | import java.io.InputStream; 26 | import java.nio.file.Path; 27 | import java.util.Properties; 28 | 29 | /** 30 | * 自动生成静态文件对应的 Content-Type 信息,弥补 vertx 的不足 31 | * @author Winter Lau 32 | */ 33 | public class AutoContentTypeStaticHandler extends StaticHandlerImpl { 34 | 35 | private final static Logger log = LoggerFactory.getLogger(AutoContentTypeStaticHandler.class); 36 | private final static Properties CONTENT_TYPES = new Properties(); 37 | private final static Path webRoot; 38 | 39 | static { 40 | String sWebroot = KooderConfig.getProperty("http.webroot"); 41 | webRoot = KooderConfig.getPath(sWebroot); 42 | try (InputStream stream = AutoContentTypeStaticHandler.class.getResourceAsStream("/mime-types.properties")) { 43 | CONTENT_TYPES.load(stream); 44 | } catch(IOException e) { 45 | log.error("Failed to loading mime-types.properties", e); 46 | } 47 | } 48 | 49 | public AutoContentTypeStaticHandler(){ 50 | super(); 51 | this.setAllowRootFileSystemAccess(true); 52 | this.setWebRoot(webRoot.toString()); 53 | } 54 | 55 | @Override 56 | public void handle(RoutingContext context) { 57 | String path = context.request().path(); 58 | int idx = path.lastIndexOf("."); 59 | String ctype = (idx > 0) ? CONTENT_TYPES.getProperty(path.substring(idx + 1).toLowerCase()) : null; 60 | if(ctype != null) 61 | context.response().putHeader("content-type", ctype); 62 | super.handle(context); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/storage/IndexStorage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.storage; 17 | 18 | import com.gitee.kooder.core.Constants; 19 | import org.apache.lucene.facet.taxonomy.TaxonomyReader; 20 | import org.apache.lucene.facet.taxonomy.TaxonomyWriter; 21 | import org.apache.lucene.index.IndexReader; 22 | import org.apache.lucene.index.IndexWriter; 23 | 24 | import java.io.IOException; 25 | import java.util.Properties; 26 | 27 | /** 28 | * 索引存储接口 29 | * @author Winter Lau 30 | */ 31 | public interface IndexStorage { 32 | 33 | /** 34 | * 对象类型和存储目录的对应关系 35 | */ 36 | Properties MAPPING_TYPES = new Properties(){{ 37 | setProperty(Constants.TYPE_REPOSITORY, "repos"); 38 | setProperty(Constants.TYPE_CODE, "code"); 39 | setProperty(Constants.TYPE_COMMIT, "commits"); 40 | setProperty(Constants.TYPE_ISSUE, "issues"); 41 | setProperty(Constants.TYPE_PR, "pulls"); 42 | setProperty(Constants.TYPE_WIKI, "wikis"); 43 | setProperty(Constants.TYPE_USER, "users"); 44 | }}; 45 | 46 | /** 47 | * 存储的唯一名称 48 | * @return 49 | */ 50 | String name(); 51 | 52 | /** 53 | * 获取索引更新的入口 54 | * @param type 55 | * @return 56 | * @exception 57 | */ 58 | IndexWriter getWriter(String type) throws IOException; 59 | 60 | /** 61 | * 获取分类数据写入入口 62 | * @param type 63 | * @return 64 | * @throws IOException 65 | */ 66 | TaxonomyWriter getTaxonomyWriter(String type) throws IOException; 67 | 68 | /** 69 | * 获取读索引的入口 70 | * @param type 71 | * @return 72 | * @exception 73 | */ 74 | IndexReader getReader(String type) throws IOException; 75 | 76 | /** 77 | * 获取分类索引的读取入口 78 | * @param type 79 | * @return 80 | * @throws IOException 81 | */ 82 | TaxonomyReader getTaxonomyReader(String type) throws IOException; 83 | 84 | } 85 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.gitee.koode 8 | kooder 9 | 1.0-beta4 10 | pom 11 | 12 | Gitee Search Engine 13 | https://gitee.com/koode/kooder 14 | 15 | 16 | UTF-8 17 | 18 | 19 | 20 | src/main/java 21 | 22 | 23 | src/main/resources 24 | 25 | 26 | src/main/java 27 | 28 | **/*.java 29 | 30 | 31 | 32 | 33 | 34 | 35 | maven-compiler-plugin 36 | 3.8.0 37 | 38 | 8 39 | 8 40 | ${project.build.sourceEncoding} 41 | 42 | 43 | 44 | 45 | maven-dependency-plugin 46 | 47 | 48 | copy 49 | install 50 | 51 | copy-dependencies 52 | 53 | 54 | ${basedir}/../lib 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | core 64 | gateway 65 | indexer 66 | 67 | 68 | 69 | 70 | Winter Lau 71 | javayou@gmail.com 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/utils/BatchTaskRunner.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.utils; 17 | 18 | import java.util.List; 19 | import java.util.concurrent.ForkJoinTask; 20 | import java.util.concurrent.RecursiveAction; 21 | import java.util.function.Consumer; 22 | 23 | /** 24 | * Batch task action 25 | * @author Winter Lau 26 | */ 27 | public final class BatchTaskRunner extends RecursiveAction { 28 | 29 | protected int threshold = 5; 30 | protected List taskList; 31 | Consumer action; 32 | 33 | /** 34 | * @param taskList 任务列表 35 | * @param threshold 每个线程处理的任务数 36 | */ 37 | private BatchTaskRunner(List taskList, int threshold, Consumer action) { 38 | this.taskList = taskList; 39 | this.threshold = threshold; 40 | this.action = action; 41 | } 42 | 43 | /** 44 | * 多线程批量执行任务 45 | * @param taskList 46 | * @param threshold 47 | * @param action 48 | */ 49 | public static void execute(List taskList, int threshold, Consumer> action) { 50 | new BatchTaskRunner(taskList, threshold, action).invoke(); 51 | } 52 | 53 | @Override 54 | protected void compute() { 55 | if (taskList.size() <= threshold) { 56 | this.action.accept(taskList); 57 | } 58 | else { 59 | this.splitFromMiddle(taskList); 60 | } 61 | } 62 | 63 | /** 64 | * 任务中分 65 | * @param list 66 | */ 67 | private void splitFromMiddle(List list) { 68 | int middle = (int)Math.ceil(list.size() / 2.0); 69 | List leftList = list.subList(0, middle); 70 | List RightList = list.subList(middle, list.size()); 71 | BatchTaskRunner left = newInstance(leftList); 72 | BatchTaskRunner right = newInstance(RightList); 73 | ForkJoinTask.invokeAll(left, right); 74 | } 75 | 76 | private BatchTaskRunner newInstance(List taskList) { 77 | return new BatchTaskRunner(taskList, threshold, action); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitea/User.java: -------------------------------------------------------------------------------- 1 | package com.gitee.kooder.gitea; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | 5 | import java.util.Date; 6 | 7 | /** 8 | * @author zhanggx 9 | */ 10 | public class User { 11 | 12 | private Integer id; 13 | private String login; 14 | private String username; 15 | private String fullName; 16 | private String email; 17 | private String avatarUrl; 18 | private String language; 19 | private Boolean isAdmin; 20 | private Boolean restricted; 21 | @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX") 22 | private Date lastLogin; 23 | @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX") 24 | private Date created; 25 | 26 | public Integer getId() { 27 | return id; 28 | } 29 | 30 | public void setId(Integer id) { 31 | this.id = id; 32 | } 33 | 34 | public String getLogin() { 35 | return login; 36 | } 37 | 38 | public void setLogin(String login) { 39 | this.login = login; 40 | } 41 | 42 | public String getUsername() { 43 | return username; 44 | } 45 | 46 | public void setUsername(String username) { 47 | this.username = username; 48 | } 49 | 50 | public String getFullName() { 51 | return fullName; 52 | } 53 | 54 | public void setFullName(String fullName) { 55 | this.fullName = fullName; 56 | } 57 | 58 | public String getEmail() { 59 | return email; 60 | } 61 | 62 | public void setEmail(String email) { 63 | this.email = email; 64 | } 65 | 66 | public String getAvatarUrl() { 67 | return avatarUrl; 68 | } 69 | 70 | public void setAvatarUrl(String avatarUrl) { 71 | this.avatarUrl = avatarUrl; 72 | } 73 | 74 | public String getLanguage() { 75 | return language; 76 | } 77 | 78 | public void setLanguage(String language) { 79 | this.language = language; 80 | } 81 | 82 | public Boolean getAdmin() { 83 | return isAdmin; 84 | } 85 | 86 | public void setAdmin(Boolean admin) { 87 | isAdmin = admin; 88 | } 89 | 90 | public Boolean getRestricted() { 91 | return restricted; 92 | } 93 | 94 | public void setRestricted(Boolean restricted) { 95 | this.restricted = restricted; 96 | } 97 | 98 | public Date getLastLogin() { 99 | return lastLogin; 100 | } 101 | 102 | public void setLastLogin(Date lastLogin) { 103 | this.lastLogin = lastLogin; 104 | } 105 | 106 | public Date getCreated() { 107 | return created; 108 | } 109 | 110 | public void setCreated(Date created) { 111 | this.created = created; 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/query/IQuery.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.query; 17 | 18 | import com.gitee.kooder.models.QueryResult; 19 | import com.gitee.kooder.models.Searchable; 20 | 21 | import java.io.IOException; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | /** 26 | * 查询接口 27 | * @author Winter Lau 28 | */ 29 | public interface IQuery { 30 | 31 | /** 32 | * 索引类型 33 | * @return 34 | */ 35 | public String type(); 36 | 37 | /** 38 | * 搜索关键字 39 | * @param key 40 | */ 41 | IQuery setSearchKey(String key); 42 | 43 | /** 44 | * 是否解析查询字符串 45 | * @param parseSearchKey 46 | * @return 47 | */ 48 | IQuery setParseSearchKey(boolean parseSearchKey); 49 | 50 | /** 51 | * 排序方法 52 | * @param sort 53 | * @return 54 | */ 55 | IQuery setSort(String sort); 56 | 57 | /** 58 | * 页码 59 | * @param page 60 | * @return 61 | */ 62 | IQuery setPage(int page); 63 | 64 | /** 65 | * 页大小 66 | * @param pageSize 67 | * @return 68 | */ 69 | IQuery setPageSize(int pageSize); 70 | 71 | /** 72 | * 添加扩展属性 73 | * @param name 74 | * @param value 75 | * @return 76 | */ 77 | IQuery addFacets(String name, String value); 78 | 79 | /** 80 | * 获取扩展属性 81 | * @return 82 | */ 83 | Map getFacets(); 84 | 85 | /** 86 | * 添加过滤条件 87 | * @param filterQueryString 88 | * @return 89 | */ 90 | IQuery addFilter(String filterQueryString); 91 | 92 | /** 93 | * 获取所有过滤条件 94 | * @return 95 | */ 96 | List getFilters(); 97 | 98 | /** 99 | * execute query 100 | * @return 101 | * @throws IOException 102 | */ 103 | QueryResult execute() throws IOException; 104 | 105 | /** 106 | * Get max object indexed 107 | * @return 108 | */ 109 | public Searchable getLastestObject() ; 110 | 111 | /** 112 | * Get total records count 113 | * @return 114 | */ 115 | public long totalCount(); 116 | } 117 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/utils/HttpUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.utils; 17 | 18 | import okhttp3.*; 19 | 20 | import java.io.IOException; 21 | import java.util.Map; 22 | import java.util.Objects; 23 | 24 | /** 25 | * http util by okhttp3 26 | * 27 | * @author zhanggx 28 | */ 29 | public class HttpUtils { 30 | 31 | public static final MediaType APPLICATION_JSON_UTF8 = MediaType.get("application/json; charset=utf-8"); 32 | 33 | private static final OkHttpClient CLIENT; 34 | 35 | private HttpUtils() { 36 | } 37 | 38 | static { 39 | CLIENT = new OkHttpClient(); 40 | } 41 | 42 | /** 43 | * http get 44 | * 45 | * @param url 46 | * @param params 47 | * @return 48 | * @throws IOException 49 | */ 50 | public static Response get(String url, Map params) throws IOException { 51 | Request request = getRequestBuilder(url, params).build(); 52 | return getResponse(request); 53 | } 54 | 55 | /** 56 | * http post application/json 57 | * 58 | * @param url 59 | * @param params 60 | * @return 61 | * @throws IOException 62 | */ 63 | public static Response postJson(String url, Map params) throws IOException { 64 | Request request = getRequestBuilder(url) 65 | .post(RequestBody.create(JsonUtils.toJson(params), APPLICATION_JSON_UTF8)) 66 | .build(); 67 | return getResponse(request); 68 | } 69 | 70 | private static Request.Builder getRequestBuilder(String url, Map params) { 71 | HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(url).newBuilder(); 72 | if (Objects.nonNull(params)) { 73 | for (Map.Entry entry : params.entrySet()) { 74 | httpUrlBuilder.addQueryParameter(entry.getKey(), entry.getValue()); 75 | } 76 | } 77 | 78 | return new Request.Builder() 79 | .url(httpUrlBuilder.build()); 80 | } 81 | 82 | private static Request.Builder getRequestBuilder(String url) { 83 | return getRequestBuilder(url, null); 84 | } 85 | 86 | 87 | private static Response getResponse(Request request) throws IOException { 88 | return CLIENT.newCall(request).execute(); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/core/SearchKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.core; 17 | 18 | import java.io.BufferedReader; 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.io.InputStreamReader; 22 | import java.util.*; 23 | 24 | /** 25 | * 搜索关键字封装对象,从关键字中提取一些固定维度信息,例如编程语言等 26 | * @author Winter Lau (javayou@gmail.com) 27 | */ 28 | public class SearchKey { 29 | 30 | private final static List languages = new ArrayList(); 31 | private String key; 32 | private Map facets; 33 | 34 | static { 35 | try ( 36 | InputStream stream = SearchKey.class.getResourceAsStream("/languages"); 37 | BufferedReader reader = new BufferedReader(new InputStreamReader(stream)) 38 | ) { 39 | do { 40 | String line = reader.readLine(); 41 | if(line == null) 42 | break; 43 | line = line.trim(); 44 | if(line.length() == 0 || line.charAt(0) == '#') 45 | continue; 46 | languages.add(line.split("/")); 47 | } while(true); 48 | } catch (IOException e) { 49 | e.printStackTrace(); 50 | } 51 | } 52 | 53 | private SearchKey(){ 54 | this.facets = new HashMap<>(); 55 | } 56 | 57 | public final static SearchKey parse(String key) { 58 | SearchKey skey = new SearchKey(); 59 | List keys = SearchHelper.splitKeywords(key); 60 | keys.removeIf( k -> { 61 | for(String[] langs : languages) { 62 | if(Arrays.binarySearch(langs, k) >= 0) { 63 | skey.facets.put("lang", k); 64 | return true; 65 | } 66 | } 67 | return false; 68 | }); 69 | skey.key = String.join(" ", keys); 70 | return skey; 71 | } 72 | 73 | public String getKey() { 74 | return key; 75 | } 76 | 77 | public Map getFacets() { 78 | return facets; 79 | } 80 | 81 | public static void main(String[] args) { 82 | SearchKey sk = SearchKey.parse("j2cache java"); 83 | System.out.println(sk.key); 84 | sk.facets.forEach((k,v)->System.out.printf("%s -> %s\n",k,v)); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/gitee/kooder/action/TaskAction.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.action; 17 | 18 | import com.gitee.kooder.core.Constants; 19 | import com.gitee.kooder.queue.QueueFactory; 20 | import com.gitee.kooder.queue.QueueTask; 21 | import io.vertx.ext.web.RoutingContext; 22 | 23 | import java.util.Arrays; 24 | 25 | /** 26 | * index task related api 27 | * @author Winter Lau 28 | */ 29 | public class TaskAction { 30 | 31 | /** 32 | * Repository index task 33 | * @param context 34 | */ 35 | public void repositories(RoutingContext context) { 36 | String jsonBody = context.getBodyAsString(); 37 | String action = getAction(context); 38 | _pushTask(Constants.TYPE_REPOSITORY, action, jsonBody); 39 | } 40 | 41 | /** 42 | * Code index task api 43 | * @param context 44 | */ 45 | public void codes(RoutingContext context) { 46 | String jsonBody = context.getBodyAsString(); 47 | String action = getAction(context); 48 | _pushTask(Constants.TYPE_CODE, action, jsonBody); 49 | } 50 | 51 | /** 52 | * Issue index task api 53 | * @param context 54 | */ 55 | public void issues(RoutingContext context) { 56 | String jsonBody = context.getBodyAsString(); 57 | String action = getAction(context); 58 | _pushTask(Constants.TYPE_ISSUE, action, jsonBody); 59 | } 60 | 61 | /** 62 | * push task to queue for later handling 63 | * @param type 64 | * @param action 65 | * @param body 66 | */ 67 | private void _pushTask(String type, String action, String body) { 68 | QueueTask task = new QueueTask(); 69 | task.setType(type); 70 | task.setAction(action); 71 | task.setJsonObjects(body); 72 | QueueFactory.getProvider().queue(task.getType()).push(Arrays.asList(task)); 73 | } 74 | 75 | /** 76 | * Turn http method to action 77 | * @param context 78 | * @return 79 | */ 80 | private String getAction(RoutingContext context) { 81 | switch(context.request().method().name()){ 82 | case "POST": 83 | case "PUT": 84 | return QueueTask.ACTION_ADD; 85 | case "UPDATE": 86 | return QueueTask.ACTION_UPDATE; 87 | case "DELETE": 88 | return QueueTask.ACTION_DELETE; 89 | } 90 | return QueueTask.ACTION_ADD; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/file/FileIndexThread.java: -------------------------------------------------------------------------------- 1 | package com.gitee.kooder.file; 2 | 3 | import com.gitee.kooder.core.Constants; 4 | import com.gitee.kooder.core.KooderConfig; 5 | import com.gitee.kooder.models.CodeRepository; 6 | import com.gitee.kooder.models.Repository; 7 | import com.gitee.kooder.queue.QueueTask; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.io.IOException; 13 | import java.nio.file.Files; 14 | import java.nio.file.Paths; 15 | import java.util.concurrent.atomic.AtomicInteger; 16 | import java.util.stream.Stream; 17 | 18 | /** 19 | * index from file 20 | * 21 | * @author zhanggx 22 | */ 23 | public class FileIndexThread extends Thread { 24 | 25 | private final static Logger log = LoggerFactory.getLogger(FileIndexThread.class); 26 | private String filePath; 27 | private String vender; 28 | 29 | public FileIndexThread() { 30 | filePath = KooderConfig.getProperty("file.index.path"); 31 | vender = KooderConfig.getProperty("file.index.vender"); 32 | 33 | if (StringUtils.isBlank(vender)) 34 | vender = "gitee"; 35 | } 36 | 37 | @Override 38 | public void run() { 39 | long start = System.currentTimeMillis(); 40 | AtomicInteger success = new AtomicInteger(), error = new AtomicInteger(); 41 | try (Stream pathStream = Files.lines(Paths.get(filePath))) { 42 | pathStream.filter(StringUtils::isNotBlank) 43 | .forEach(repoUrl -> { 44 | try { 45 | Repository repo = new Repository(); 46 | repo.setId(repoUrl.hashCode()); 47 | repo.setName(repoUrl.substring(repoUrl.lastIndexOf("/") + 1, repoUrl.lastIndexOf(".git"))); 48 | repo.setUrl(repoUrl); 49 | QueueTask.add(Constants.TYPE_REPOSITORY, repo); 50 | CodeRepository codes = new CodeRepository(); 51 | codes.setId(repo.getId()); 52 | codes.setScm(CodeRepository.SCM_GIT); 53 | codes.setName(repo.getName()); 54 | codes.setUrl(repo.getUrl()); 55 | codes.setVender(vender); 56 | QueueTask.add(Constants.TYPE_CODE, codes); 57 | success.incrementAndGet(); 58 | } catch (Exception e) { 59 | error.incrementAndGet(); 60 | log.warn("{} index error:", repoUrl, e.getMessage()); 61 | } 62 | }); 63 | log.info( 64 | "index from file: {} repositories indexed, success: {}, error: {}, using {} ms", 65 | success.get() + error.get(), 66 | success.get(), 67 | error.get(), 68 | System.currentTimeMillis() - start 69 | ); 70 | } catch (IOException e) { 71 | log.error("fail to index from file", e); 72 | } 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /core/src/main/resources/jcseg.properties: -------------------------------------------------------------------------------- 1 | # Jcseg properties file. 2 | # @Note: 3 | # true | 1 | on for open the specified configuration or 4 | # false | 0 | off to close it. 5 | # bug report chenxin 6 | 7 | # Jcseg function 8 | #maximum match length. (5-7) 9 | jcseg.maxlen = 7 10 | 11 | #Whether to recognized the Chinese name. 12 | jcseg.icnname = true 13 | 14 | #maximum length for pair punctuation text. 15 | jcseg.pptmaxlen = 7 16 | 17 | #maximum length for Chinese last name andron. 18 | jcseg.cnmaxlnadron = 1 19 | 20 | #Whether to clear the stopwords. 21 | jcseg.clearstopword = true 22 | 23 | #Whether to convert the Chinese numeric to Arabic number. like '\u4E09\u4E07' to 30000. 24 | jcseg.cnnumtoarabic = true 25 | 26 | #Whether to convert the Chinese fraction to Arabic fraction. 27 | #@Note: for lucene,solr,elasticsearch eg.. close it. 28 | jcseg.cnfratoarabic = false 29 | 30 | #Whether to keep the unrecognized word. 31 | jcseg.keepunregword = true 32 | 33 | #Whether to do the secondary segmentation for the complex English words 34 | jcseg.ensecondseg = true 35 | 36 | #minimum length of the secondary segmentation token. 37 | jcseg.ensecminlen = 2 38 | 39 | #Whether to do the English word segmentation 40 | #the jcseg.ensecondseg must set to true before active this function 41 | jcseg.enwordseg = false 42 | 43 | #maximum match length for English extracted word 44 | jcseg.enmaxlen = 16 45 | 46 | #threshold for Chinese name recognize. 47 | # better not change it before you know what you are doing. 48 | jcseg.nsthreshold = 1000000 49 | 50 | #The punctuation set that will be keep in an token.(Not the end of the token). 51 | jcseg.keeppunctuations = @#%.&+ 52 | 53 | #Whether to append the pinyin of the entry. 54 | jcseg.appendpinyin = true 55 | 56 | #Whether to load and append the synonyms words of the entry. 57 | jcseg.appendsyn = true 58 | 59 | 60 | ####for Tokenizer 61 | #default delimiter for JcsegDelimiter tokenizer 62 | #set to default or whitespace will use the default whitespace as delimiter 63 | #or set to the char you want, like ',' or whatever 64 | jcseg.delimiter = default 65 | 66 | #default length for the N-gram tokenizer 67 | jcseg.gram = 1 68 | 69 | 70 | ####about the lexicon 71 | #absolute path of the lexicon file. 72 | #Multiple path support from jcseg 1.9.2, use ';' to split different path. 73 | #example: lexicon.path = /home/chenxin/lex1;/home/chenxin/lex2 (Linux) 74 | # : lexicon.path = D:/jcseg/lexicon/1;D:/jcseg/lexicon/2 (WinNT) 75 | #lexicon.path=/Code/java/JavaSE/jcseg/lexicon 76 | #lexicon.path = {jar.dir}/lexicon ({jar.dir} means the base directory of jcseg-core-{version}.jar) 77 | #@since 1.9.9 Jcseg default to load the lexicons in the classpath 78 | lexicon.path = null 79 | 80 | #Whether to load the modified lexicon file auto. 81 | lexicon.autoload = false 82 | 83 | #Poll time for auto load. (seconds) 84 | lexicon.polltime = 300 85 | 86 | 87 | ####lexicon load 88 | #Whether to load the part of speech of the entry. 89 | jcseg.loadpos = true 90 | 91 | #Whether to load the pinyin of the entry. 92 | jcseg.loadpinyin = true 93 | 94 | #Whether to load the synonyms words of the entry. 95 | jcseg.loadsyn = true 96 | 97 | #Whether to load the entity of the entry 98 | jcseg.loadentity = true 99 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/jcseg/JcsegTokenizer.java: -------------------------------------------------------------------------------- 1 | package com.gitee.kooder.jcseg; 2 | 3 | import java.io.IOException; 4 | 5 | import org.apache.lucene.analysis.Tokenizer; 6 | import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; 7 | import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; 8 | import org.apache.lucene.analysis.tokenattributes.TypeAttribute; 9 | import org.lionsoul.jcseg.ISegment; 10 | import org.lionsoul.jcseg.IWord; 11 | import org.lionsoul.jcseg.dic.ADictionary; 12 | import org.lionsoul.jcseg.segmenter.SegmenterConfig; 13 | 14 | 15 | /** 16 | *

17 | * here is the documentation from {@link Tokenizer} 18 | * A Tokenizer is a TokenStream whose input is a Reader. 19 | *

20 | * 21 | *

22 | * This is an abstract class; subclasses must override {@link #incrementToken()} 23 | *

24 | * 25 | *

26 | * NOTE: Subclasses overriding {@link #incrementToken()} must 27 | * call {@link #clearAttributes()} before setting attributes 28 | *

29 | * 30 | *

31 | * lucene invoke Tokenizer#setReader(Reader input) to set the inputPending 32 | * after invoke the reset, global object input will be available 33 | *

34 | * 35 | *

jcseg tokenizer for lucene on or after 5.1.0

36 | * 37 | * @author chenxin 38 | */ 39 | public class JcsegTokenizer extends Tokenizer 40 | { 41 | // The default Jcseg segmentor 42 | private final ISegment segmentor; 43 | 44 | private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); 45 | private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); 46 | private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class); 47 | 48 | /** 49 | * field level offset tracker for multiple-value field 50 | * like the Array field in Elasticsearch or Solr 51 | */ 52 | private int fieldOffset = 0; 53 | 54 | public JcsegTokenizer( 55 | ISegment.Type type, 56 | SegmenterConfig config, 57 | ADictionary dic ) 58 | { 59 | segmentor = type.factory.create(config, dic); 60 | } 61 | 62 | @Override 63 | final public boolean incrementToken() throws IOException 64 | { 65 | /* Clear the attributes */ 66 | clearAttributes(); 67 | 68 | final IWord word = segmentor.next(); 69 | if ( word == null ) { 70 | fieldOffset = offsetAtt.endOffset(); 71 | return false; 72 | } 73 | 74 | // char[] token = word.getValue().toCharArray(); 75 | // termAtt.copyBuffer(token, 0, token.length); 76 | termAtt.append(word.getValue()); 77 | termAtt.setLength(word.getLength()); 78 | offsetAtt.setOffset( 79 | correctOffset(fieldOffset + word.getPosition()), 80 | correctOffset(fieldOffset + word.getPosition() + word.getLength()) 81 | ); 82 | typeAtt.setType("word"); 83 | 84 | return true; 85 | } 86 | 87 | @Override 88 | public void end() throws IOException 89 | { 90 | super.end(); 91 | final int finalOffset = correctOffset(fieldOffset); 92 | offsetAtt.setOffset(finalOffset, finalOffset); 93 | this.fieldOffset = 0; 94 | } 95 | 96 | @Override 97 | public void reset() throws IOException 98 | { 99 | super.reset(); 100 | segmentor.reset(input); 101 | offsetAtt.setOffset(0, 0); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/gitee/kooder/action/SearchActionBase.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.action; 17 | 18 | import com.gitee.kooder.core.Constants; 19 | import com.gitee.kooder.models.QueryResult; 20 | import com.gitee.kooder.query.QueryFactory; 21 | import com.gitee.kooder.server.Action; 22 | import io.vertx.ext.web.RoutingContext; 23 | import org.apache.commons.lang3.StringUtils; 24 | 25 | import java.io.IOException; 26 | import java.util.Arrays; 27 | 28 | /** 29 | * Action for search, both for web and api 30 | * @author Winter Lau 31 | */ 32 | interface SearchActionBase extends Action { 33 | 34 | /** 35 | * execute search 36 | * @param context 37 | * @param type 38 | * @return 39 | * @throws IOException 40 | */ 41 | default QueryResult _search(RoutingContext context, String type) throws IOException { 42 | String q = param(context.request(), "q"); 43 | if(StringUtils.isBlank(q)) 44 | return null; 45 | 46 | QueryResult result = null; 47 | int page = Math.max(1, param(context.request(),"p", 1)); 48 | 49 | String sort = param(context.request(), "sort"); 50 | String lang = param(context.request(), Constants.FIELD_LANGUAGE); 51 | 52 | switch (type) { 53 | case Constants.TYPE_REPOSITORY: 54 | result = QueryFactory.REPO() 55 | .setEnterpriseId(param(context.request(), Constants.FIELD_ENTERPRISE_ID, 0)) 56 | .setSearchKey(q) 57 | .addFacets(Constants.FIELD_LANGUAGE, lang) 58 | .setSort(sort) 59 | .setPage(page) 60 | .setPageSize(PAGE_SIZE) 61 | .execute(); 62 | break; 63 | 64 | case Constants.TYPE_ISSUE: 65 | result = QueryFactory.ISSUE() 66 | .setEnterpriseId(param(context.request(), Constants.FIELD_ENTERPRISE_ID, 0)) 67 | .setSearchKey(q) 68 | .setSort(sort) 69 | .setPage(page) 70 | .setPageSize(PAGE_SIZE) 71 | .execute(); 72 | break; 73 | 74 | case Constants.TYPE_CODE: 75 | result = QueryFactory.CODE() 76 | .setSearchKey(q) 77 | .addFacets(Constants.FIELD_LANGUAGE, lang) 78 | .addFacets(Constants.FIELD_REPO_NAME, param(context.request(), Constants.FIELD_REPO_NAME)) 79 | .addFacets(Constants.FIELD_CODE_OWNER, param(context.request(), Constants.FIELD_CODE_OWNER)) 80 | .setSort(sort) 81 | .setPage(page) 82 | .setPageSize(PAGE_SIZE) 83 | .execute(); 84 | } 85 | return result; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/models/Searchable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.models; 17 | 18 | import com.gitee.kooder.core.Constants; 19 | import org.apache.commons.lang3.math.NumberUtils; 20 | import org.apache.lucene.document.*; 21 | import org.apache.lucene.facet.FacetField; 22 | 23 | import java.io.Serializable; 24 | 25 | /** 26 | * Searchable object 27 | * @author Winter Lau 28 | */ 29 | public abstract class Searchable implements Serializable { 30 | 31 | protected long id; // object id , ex: repo id, issue id 32 | protected int _doc_id; // document id 33 | protected float _doc_score; // document score 34 | 35 | public long getId() { 36 | return id; 37 | } 38 | 39 | public void setId(long id) { 40 | this.id = id; 41 | } 42 | 43 | public int get_doc_id() { 44 | return _doc_id; 45 | } 46 | 47 | public void set_doc_id(int _doc_id) { 48 | this._doc_id = _doc_id; 49 | } 50 | 51 | public float get_doc_score() { 52 | return _doc_score; 53 | } 54 | 55 | public void set_doc_score(float _doc_score) { 56 | this._doc_score = _doc_score; 57 | } 58 | 59 | protected Document newDocument() { 60 | Document doc = new Document(); 61 | doc.add(new NumericDocValuesField(Constants.FIELD_ID, id)); 62 | doc.add(new StringField(Constants.FIELD_ID, String.valueOf(id), Field.Store.YES)); 63 | return doc; 64 | } 65 | 66 | /** 67 | * generate lucene document 68 | * @return 69 | */ 70 | public abstract Document getDocument() ; 71 | 72 | /** 73 | * Read fields from document 74 | * @param doc 75 | */ 76 | public abstract Searchable setDocument(Document doc) ; 77 | 78 | /** 79 | * 读取数值字段 80 | * @param doc 81 | * @param fieldName 82 | * @param defValues 83 | * @return 84 | */ 85 | public final static int getIntField(Document doc, String fieldName, int...defValues) { 86 | int def = (defValues.length > 0)?defValues[0]:-1; 87 | return NumberUtils.toInt(doc.get(fieldName), def); 88 | } 89 | 90 | protected void addLongToDoc(Document doc, String fn, long fv) { 91 | doc.add(new LongPoint(fn, fv)); 92 | doc.add(new StoredField(fn, String.valueOf(fv))); 93 | } 94 | 95 | protected void addIntToDoc(Document doc, String fn, int fv) { 96 | doc.add(new IntPoint(fn, fv)); 97 | doc.add(new StoredField(fn, String.valueOf(fv))); 98 | } 99 | 100 | protected void addFacetToDoc(Document doc, String fn, String fv) { 101 | doc.add(new FacetField(fn, fv)); 102 | doc.add(new StringField(fn, fv, Field.Store.YES)); 103 | } 104 | 105 | protected void addNumToDoc(Document doc, String fn, long fv) { 106 | doc.add(new NumericDocValuesField(fn, fv)); 107 | doc.add(new StoredField(fn, fv)); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/storage/StorageFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.storage; 17 | 18 | import com.gitee.kooder.core.KooderConfig; 19 | import org.apache.commons.lang3.math.NumberUtils; 20 | import org.apache.lucene.facet.taxonomy.TaxonomyReader; 21 | import org.apache.lucene.facet.taxonomy.TaxonomyWriter; 22 | import org.apache.lucene.index.IndexReader; 23 | import org.apache.lucene.index.IndexWriter; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | import java.io.IOException; 28 | import java.nio.file.Path; 29 | import java.util.Properties; 30 | 31 | /** 32 | * 索引以及仓库存储管理工厂类 33 | * @author Winter Lau 34 | */ 35 | public class StorageFactory { 36 | 37 | private final static Logger log = LoggerFactory.getLogger(StorageFactory.class); 38 | 39 | private static IndexStorage storage; //index storage 40 | private static Path repositoriesPath; 41 | private static int repositoriesMaxSizeInGigabyte = 100;//unit:G 42 | 43 | static { 44 | Properties props = KooderConfig.getStoragePropertes(); 45 | try { 46 | if("disk".equalsIgnoreCase(props.getProperty("type").trim())) { 47 | storage = new DiskIndexStorage(props); 48 | } 49 | String repoPath = props.getProperty("repositories.path"); 50 | repositoriesPath = KooderConfig.checkAndCreatePath(repoPath); 51 | } catch (IOException e) { 52 | log.error("Failed to initialize storage manager.", e); 53 | } 54 | repositoriesMaxSizeInGigabyte = NumberUtils.toInt(props.getProperty("repositories.max_size_in_gigabyte"), 100); 55 | } 56 | 57 | /** 58 | * 返回仓库的存储路径 59 | * 1234/5678/J2Cache_12345678 60 | * @param path 61 | * @return 62 | */ 63 | public static Path getRepositoryPath(String path) { 64 | return repositoriesPath.resolve(path); 65 | } 66 | 67 | /** 68 | * 获取索引更新的入口 69 | * @param type 70 | * @return 71 | * @exception 72 | */ 73 | public static IndexWriter getIndexWriter(String type) throws IOException { 74 | return storage.getWriter(type); 75 | } 76 | 77 | /** 78 | * 获取读索引的入口 79 | * @param type 80 | * @return 81 | * @exception 82 | */ 83 | public static IndexReader getIndexReader(String type) throws IOException { 84 | return storage.getReader(type); 85 | } 86 | 87 | /** 88 | * 获取分类数据写入入口 89 | * @param type 90 | * @return 91 | * @throws IOException 92 | */ 93 | public static TaxonomyWriter getTaxonomyWriter(String type) throws IOException { 94 | return storage.getTaxonomyWriter(type); 95 | } 96 | 97 | /** 98 | * 获取分类索引的读取入口 99 | * @param type 100 | * @return 101 | * @throws IOException 102 | */ 103 | public static TaxonomyReader getTaxonomyReader(String type) throws IOException { 104 | return storage.getTaxonomyReader(type); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/code/SourceCodeAnalyzer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.code; 17 | 18 | import com.gitee.kooder.query.QueryException; 19 | import org.apache.commons.lang3.StringUtils; 20 | import org.apache.lucene.analysis.Analyzer; 21 | import org.apache.lucene.analysis.TokenStream; 22 | import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; 23 | import org.apache.lucene.queryparser.classic.ParseException; 24 | import org.apache.lucene.queryparser.classic.QueryParser; 25 | import org.apache.lucene.search.Query; 26 | import org.apache.lucene.search.highlight.*; 27 | 28 | import java.io.IOException; 29 | import java.io.StringReader; 30 | import java.util.ArrayList; 31 | import java.util.List; 32 | 33 | /** 34 | * Source code analyzer 35 | * @author Winter Lau 36 | */ 37 | public class SourceCodeAnalyzer extends Analyzer { 38 | 39 | private final static String separatorChars = "~!@#$%^&*()-_+[]{}?/\\<>.;,'\"\r\n\t"; 40 | private final static String replaceChars = StringUtils.repeat(' ', separatorChars.length()); 41 | private final static Formatter hl_fmt = new SimpleHTMLFormatter("", ""); 42 | 43 | @Override 44 | protected TokenStreamComponents createComponents(String s) { 45 | return new TokenStreamComponents(new SourceCodeTokenizer()); 46 | } 47 | 48 | /** 49 | * extract text content to tokens 50 | * @param code 51 | * @return 52 | */ 53 | public List tokens(String code) { 54 | List tokens = new ArrayList<>(); 55 | try (TokenStream stream = tokenStream(null, new StringReader(code))){ 56 | CharTermAttribute termAtt = stream.addAttribute(CharTermAttribute.class); 57 | stream.reset(); 58 | while (stream.incrementToken()) { 59 | tokens.add(termAtt.toString()); 60 | } 61 | stream.end(); 62 | } catch (IOException e) { 63 | throw new QueryException("Failed to tokens: " + code, e); 64 | } 65 | return tokens; 66 | } 67 | 68 | /** 69 | * Highlight code 70 | * @param code 71 | * @param key 72 | * @return 73 | * @throws Exception 74 | */ 75 | public String highlight(String code, String key) { 76 | try { 77 | key = StringUtils.replaceChars(key, separatorChars, replaceChars); 78 | QueryParser parser = new QueryParser(null, this); 79 | Query query = parser.parse(key); 80 | QueryScorer scorer = new QueryScorer(query); 81 | Highlighter hig = new Highlighter(hl_fmt, scorer); 82 | TokenStream tokens = this.tokenStream(null, new StringReader(code)); 83 | String[] fragments = hig.getBestFragments(tokens, code, hig.getMaxDocCharsToAnalyze()); 84 | return String.join( "", fragments); 85 | } catch (ParseException e) { 86 | String escape_key = QueryParser.escape(key); 87 | if(StringUtils.equals(key, escape_key)) 88 | return code; 89 | return highlight(code, escape_key); 90 | } catch (IOException | InvalidTokenOffsetsException e) { 91 | return code; 92 | } 93 | } 94 | 95 | } -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/utils/JsonUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.utils; 17 | 18 | import com.fasterxml.jackson.annotation.JsonInclude; 19 | import com.fasterxml.jackson.core.JsonProcessingException; 20 | import com.fasterxml.jackson.core.type.TypeReference; 21 | import com.fasterxml.jackson.databind.DeserializationFeature; 22 | import com.fasterxml.jackson.databind.JsonNode; 23 | import com.fasterxml.jackson.databind.ObjectMapper; 24 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 25 | import org.apache.commons.lang3.math.NumberUtils; 26 | 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | 30 | /** 31 | * JSON 工具包 32 | * @author Winter Lau 33 | */ 34 | public class JsonUtils { 35 | 36 | private final static ObjectMapper JSON = new ObjectMapper(); 37 | 38 | static { 39 | JSON.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 40 | JSON.setSerializationInclusion(JsonInclude.Include.NON_NULL); 41 | JSON.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); 42 | } 43 | 44 | /** 45 | * 读取文本json节点数据 46 | * @param node 47 | * @param fn 48 | * @return 49 | */ 50 | public static String jsonAttr(JsonNode node, String fn) { 51 | JsonNode f = node.get(fn); 52 | return (f != null) ? f.textValue() : null; 53 | } 54 | 55 | /** 56 | * 读取数值 json 节点数据 57 | * @param node 58 | * @param fn 59 | * @param defaultValue 60 | * @return 61 | */ 62 | public static long jsonAttr(JsonNode node, String fn, long defaultValue) { 63 | JsonNode f = node.get(fn); 64 | return (f!=null)?NumberUtils.toLong(f.textValue(), defaultValue):defaultValue; 65 | } 66 | 67 | /** 68 | * 对象转 JSON 字符串 69 | * @param obj 70 | * @return 71 | */ 72 | public static String toJson(Object obj) { 73 | try { 74 | return JSON.writeValueAsString(obj); 75 | } catch(JsonProcessingException e) { 76 | e.printStackTrace(); 77 | } 78 | return null; 79 | } 80 | 81 | /** 82 | * 解析 JSON 到对象 83 | * @param content 84 | * @param valueType 85 | * @param 86 | * @return 87 | */ 88 | public static T readValue(String content, Class valueType) { 89 | try { 90 | return JSON.readValue(content, valueType); 91 | } catch(JsonProcessingException e) { 92 | e.printStackTrace(); 93 | } 94 | return null; 95 | } 96 | 97 | public static T readValue(String content, TypeReference valueTypeRef) { 98 | try { 99 | return JSON.readValue(content, valueTypeRef); 100 | } catch(JsonProcessingException e) { 101 | e.printStackTrace(); 102 | } 103 | return null; 104 | } 105 | 106 | public static T readValue(InputStream src, TypeReference valueTypeRef) { 107 | try { 108 | return JSON.readValue(src, valueTypeRef); 109 | } catch(IOException e) { 110 | e.printStackTrace(); 111 | } 112 | return null; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/code/CodeFileTraveler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.code; 17 | 18 | import com.gitee.kooder.core.Constants; 19 | import com.gitee.kooder.index.IndexManager; 20 | import com.gitee.kooder.models.SourceFile; 21 | import org.apache.lucene.document.Document; 22 | import org.apache.lucene.document.NumericDocValuesField; 23 | import org.apache.lucene.facet.FacetsConfig; 24 | import org.apache.lucene.facet.taxonomy.TaxonomyWriter; 25 | import org.apache.lucene.index.IndexWriter; 26 | import org.apache.lucene.index.Term; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | import java.io.IOException; 31 | 32 | /** 33 | * Travel all file in repository and build index for it 34 | * @author Winter Lau 35 | */ 36 | public class CodeFileTraveler implements FileTraveler { 37 | 38 | private final static Logger log = LoggerFactory.getLogger(CodeFileTraveler.class); 39 | 40 | private IndexWriter writer; 41 | private TaxonomyWriter taxonomyWriter; 42 | 43 | public CodeFileTraveler(IndexWriter writer, TaxonomyWriter taxonomyWriter) { 44 | this.writer = writer; 45 | this.taxonomyWriter = taxonomyWriter; 46 | } 47 | 48 | /** 49 | * update source file index 50 | * 51 | * @param codeid document object 52 | */ 53 | @Override 54 | public void updateDocument(SourceFile codeid) { 55 | try { 56 | Document doc = buildFacetDocument(codeid.getDocument()); 57 | writer.updateDocument(new Term(Constants.FIELD_UUID, codeid.getUuid()), doc); 58 | } catch (IllegalArgumentException | IOException e) { 59 | log.error("Failed to update ducment: file:" + codeid.getName() + " in repo:" + codeid.getRepository().getName(), e); 60 | } 61 | } 62 | 63 | /** 64 | * Delete single file document 65 | * 66 | * @param codeid 67 | * @return 68 | */ 69 | @Override 70 | public void deleteDocument(SourceFile codeid) { 71 | //log.info("deleteDocument:" + codeid); 72 | try { 73 | writer.deleteDocuments(new Term(Constants.FIELD_UUID, codeid.getUuid())); 74 | } catch (IOException e) { 75 | log.error("Failed to delete ducment with uuid = " + codeid.getUuid(), e); 76 | } 77 | } 78 | 79 | /** 80 | * Clear all file document belong to one repository 81 | * 82 | * @param repoId 83 | */ 84 | @Override 85 | public void resetRepository(long repoId) { 86 | //log.info("resetRepository:" + repoId); 87 | try { 88 | writer.deleteDocuments(NumericDocValuesField.newSlowExactQuery(Constants.FIELD_REPO_ID, repoId)); 89 | } catch (IOException e) { 90 | log.error("Failed to reset repository with id = " + repoId, e); 91 | } 92 | } 93 | 94 | /** 95 | * Building facet document 96 | * @param doc 97 | * @return 98 | * @throws IOException 99 | */ 100 | private Document buildFacetDocument(Document doc) throws IOException { 101 | FacetsConfig facetsConfig = IndexManager.facetsConfig; 102 | return facetsConfig.build(taxonomyWriter, doc); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/query/IssueQuery.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.query; 17 | 18 | import com.gitee.kooder.core.AnalyzerFactory; 19 | import com.gitee.kooder.core.Constants; 20 | import org.apache.lucene.index.Term; 21 | import org.apache.lucene.queryparser.classic.ParseException; 22 | import org.apache.lucene.queryparser.classic.QueryParser; 23 | import org.apache.lucene.search.*; 24 | 25 | /** 26 | * Issue 搜索 27 | * @author Winter Lau 28 | */ 29 | public class IssueQuery extends QueryBase { 30 | /** 31 | * 索引类型 32 | * 33 | * @return 34 | */ 35 | @Override 36 | public String type() { 37 | return Constants.TYPE_ISSUE; 38 | } 39 | 40 | /** 41 | * Last issue based on created_at field 42 | * Because gitlab issue api doesn't support filter by id 43 | * @return 44 | */ 45 | @Override 46 | protected Sort getLastestObjectSort() { 47 | return new Sort(new SortField(Constants.FIELD_CREATED_AT, SortField.Type.LONG, true)); 48 | } 49 | 50 | /** 51 | * 构建查询对象 52 | * 53 | * @return 54 | */ 55 | @Override 56 | protected Query buildUserQuery() { 57 | if(parseSearchKey) { 58 | QueryParser parser = new QueryParser("issue", AnalyzerFactory.getInstance(false)); 59 | try { 60 | return parser.parse(searchKey); 61 | } catch (ParseException e) { 62 | throw new QueryException("Failed to parse \""+searchKey+"\"", e); 63 | } 64 | } 65 | String q = QueryParser.escape(searchKey); 66 | BooleanQuery.Builder builder = new BooleanQuery.Builder(); 67 | //filter 68 | //search 69 | BooleanQuery.Builder qbuilder = new BooleanQuery.Builder(); 70 | qbuilder.add(makeBoostQuery(Constants.FIELD_IDENT, q, 100.0f), BooleanClause.Occur.SHOULD); 71 | qbuilder.add(makeBoostQuery(Constants.FIELD_TITLE, q, 10.0f), BooleanClause.Occur.SHOULD); 72 | qbuilder.add(makeBoostQuery(Constants.FIELD_TAGS, q, 1.0f), BooleanClause.Occur.SHOULD); 73 | qbuilder.add(makeBoostQuery(Constants.FIELD_DESC, q, 1.0f), BooleanClause.Occur.SHOULD); 74 | qbuilder.setMinimumNumberShouldMatch(1); 75 | 76 | builder.add(qbuilder.build(), BooleanClause.Occur.MUST); 77 | 78 | return builder.build(); 79 | } 80 | 81 | /** 82 | * 对搜索加权 83 | * @param field 84 | * @param q 85 | * @param boost 86 | * @return 87 | */ 88 | @Override 89 | protected BoostQuery makeBoostQuery(String field, String q, float boost) { 90 | if("ident".equals(field)) 91 | return new BoostQuery(new TermQuery(new Term(Constants.FIELD_IDENT, q)), boost); 92 | return super.makeBoostQuery(field, q, boost); 93 | } 94 | 95 | /** 96 | * 构建排序对象 97 | * 98 | * @return 99 | */ 100 | @Override 101 | protected Sort buildSort() { 102 | if("create".equals(sort)) 103 | return new Sort(new SortedNumericSortField(Constants.FIELD_CREATED_AT, SortField.Type.LONG, true)); 104 | if("update".equals(sort)) 105 | return new Sort(new SortedNumericSortField(Constants.FIELD_UPDATED_AT, SortField.Type.LONG, true)); 106 | return Sort.RELEVANCE; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/queue/EmbedQueueProvider.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.queue; 17 | 18 | import com.gitee.kooder.core.KooderConfig; 19 | import org.apache.commons.lang3.math.NumberUtils; 20 | import org.infobip.lib.popout.FileQueue; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import java.io.IOException; 25 | import java.nio.file.Files; 26 | import java.nio.file.Path; 27 | import java.util.*; 28 | import java.util.concurrent.ConcurrentHashMap; 29 | 30 | /** 31 | * 实现 Gitee Search 内嵌式的队列,不依赖第三方服务,通过 HTTP 方式提供对象获取 32 | * @author Winter Lau 33 | */ 34 | public class EmbedQueueProvider implements QueueProvider { 35 | 36 | private final static Logger log = LoggerFactory.getLogger(EmbedQueueProvider.class); 37 | 38 | private Map> fileQueues = new ConcurrentHashMap<>(); 39 | 40 | public EmbedQueueProvider(Properties props) { 41 | int batch_size = NumberUtils.toInt(props.getProperty("embed.batch_size", "10000"), 10000); 42 | 43 | Path path = checkoutPath(KooderConfig.getPath(props.getProperty("embed.path"))); 44 | for(String type : getAllTypes()) { 45 | Path typePath = checkoutPath(path.resolve(type)); 46 | fileQueues.put(type, FileQueue.batched().name(type) 47 | .folder(typePath) 48 | .restoreFromDisk(true) 49 | .batchSize(batch_size) 50 | .build()); 51 | } 52 | } 53 | 54 | private static Path checkoutPath(Path path) { 55 | if(!Files.exists(path) || !Files.isDirectory(path)) { 56 | log.warn("Path '{}' for queue storage not exists, created it!", path); 57 | try { 58 | Files.createDirectories(path); 59 | } catch(IOException e) { 60 | log.error("Failed to create directory '{}'", path, e); 61 | } 62 | } 63 | return path; 64 | } 65 | 66 | /** 67 | * 队列的唯一名称 68 | * 69 | * @return 70 | */ 71 | @Override 72 | public String name() { 73 | return "embed"; 74 | } 75 | 76 | /** 77 | * 获取某个任务类型的队列 78 | * 79 | * @param type 80 | * @return 81 | */ 82 | @Override 83 | public Queue queue(String type) { 84 | return new Queue() { 85 | @Override 86 | public String type() { 87 | return type; 88 | } 89 | 90 | @Override 91 | public void push(Collection tasks) { 92 | fileQueues.get(type).addAll(tasks); 93 | } 94 | 95 | @Override 96 | public List pop(int count) { 97 | List tasks = new ArrayList<>(); 98 | QueueTask task; 99 | while(tasks.size() < count && (task = fileQueues.get(type).poll()) != null) 100 | tasks.add(task); 101 | return tasks; 102 | } 103 | 104 | @Override 105 | public void close() { 106 | fileQueues.get(type).close(); 107 | } 108 | }; 109 | } 110 | 111 | @Override 112 | public void close() { 113 | fileQueues.values().forEach(q -> q.close()); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/code/RepositoryManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.code; 17 | 18 | import com.gitee.kooder.core.Constants; 19 | import com.gitee.kooder.index.IndexException; 20 | import com.gitee.kooder.models.CodeRepository; 21 | import com.gitee.kooder.storage.StorageFactory; 22 | import org.apache.lucene.document.*; 23 | import org.apache.lucene.index.IndexNotFoundException; 24 | import org.apache.lucene.index.IndexReader; 25 | import org.apache.lucene.index.IndexWriter; 26 | import org.apache.lucene.index.Term; 27 | import org.apache.lucene.search.IndexSearcher; 28 | import org.apache.lucene.search.TermQuery; 29 | import org.apache.lucene.search.TopDocs; 30 | 31 | import java.io.IOException; 32 | 33 | /** 34 | * 用于管理源代码仓库元信息 35 | * @author Winter Lau 36 | */ 37 | public interface RepositoryManager { 38 | 39 | RepositoryManager INSTANCE = new LuceneRepositoryManager(); 40 | 41 | /** 42 | * 根据仓库的编号获取仓库元信息 43 | * @param id 44 | * @return 45 | */ 46 | CodeRepository get(long id); 47 | 48 | /** 49 | * 保存仓库元信息,如果存在 id 相同的则覆盖更新 50 | * @param repo 51 | */ 52 | void save(CodeRepository repo); 53 | 54 | /** 55 | * 删除元信息 56 | * @param id 57 | * @return 58 | */ 59 | boolean delete(long id); 60 | 61 | } 62 | 63 | /** 64 | * 使用 Lucene 索引库来保存代码仓库元信息 65 | * @author Winter Lau 66 | */ 67 | class LuceneRepositoryManager implements RepositoryManager { 68 | 69 | @Override 70 | public CodeRepository get(long id) { 71 | try (IndexReader reader = StorageFactory.getIndexReader(Constants.TYPE_METADATA)) { 72 | IndexSearcher searcher = new IndexSearcher(reader); 73 | TopDocs docs = searcher.search(new TermQuery(new Term(Constants.FIELD_REPO_ID, String.valueOf(id))), 1); 74 | if (docs.totalHits.value == 0) 75 | return null; 76 | Document doc = reader.document(docs.scoreDocs[0].doc); 77 | return new CodeRepository().setDocument(doc); 78 | } catch (IndexNotFoundException e) { 79 | return null; 80 | } catch (IOException e) { 81 | throw new IndexException("Failed to get repo in metedata db : id = " + id, e); 82 | } 83 | } 84 | 85 | @Override 86 | public void save(CodeRepository repo) { 87 | synchronized (this){ //不支持并发写入 88 | try (IndexWriter writer = StorageFactory.getIndexWriter(Constants.TYPE_METADATA)) { 89 | Document doc = repo.getDocument(); 90 | writer.updateDocument(new Term(Constants.FIELD_REPO_ID, repo.getIdAsString()), doc); 91 | } catch (IOException e) { 92 | throw new IndexException("Failed to save repo in metedata db : " + repo, e); 93 | } 94 | } 95 | } 96 | 97 | @Override 98 | public boolean delete(long id) { 99 | synchronized (this) { 100 | try (IndexWriter writer = StorageFactory.getIndexWriter(Constants.TYPE_METADATA)) { 101 | writer.deleteDocuments(new Term(Constants.FIELD_REPO_ID, String.valueOf(id))); 102 | } catch (IOException e) { 103 | throw new IndexException("Failed to delete repo from metedata db, id = " + id, e); 104 | } 105 | return false; 106 | } 107 | } 108 | 109 | } -------------------------------------------------------------------------------- /gateway/src/main/java/com/gitee/kooder/server/Action.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.server; 17 | 18 | import com.gitee.kooder.queue.QueueTask; 19 | import io.netty.handler.codec.http.HttpResponseStatus; 20 | import io.vertx.core.MultiMap; 21 | import io.vertx.core.http.HttpServerRequest; 22 | import io.vertx.core.http.HttpServerResponse; 23 | import io.vertx.ext.web.RoutingContext; 24 | import org.apache.commons.lang3.math.NumberUtils; 25 | 26 | import java.util.Collections; 27 | import java.util.HashMap; 28 | import java.util.List; 29 | import java.util.Map; 30 | 31 | /** 32 | * Action base 33 | * Each request has one independent action instance 34 | * @author Winter Lau 35 | */ 36 | public interface Action { 37 | 38 | int PAGE_SIZE = 20; //结果集每页显示的记录数 39 | 40 | /** 41 | * invoke velocity template 42 | * @param context 43 | * @param vm 44 | * @param params 45 | */ 46 | default void vm(RoutingContext context, String vm, Map params) { 47 | String content = TemplateEngine.render(vm, params, context); 48 | context.response().putHeader("Content-Type", "text/html; charset=UTF-8").send(content); 49 | } 50 | 51 | /** 52 | * Render velocity template 53 | * @param context 54 | * @param vm 55 | */ 56 | default void vm(RoutingContext context, String vm) { 57 | vm(context, vm, null); 58 | } 59 | 60 | /** 61 | * Redirect to a new url 62 | * @param context 63 | * @param newUrl 64 | */ 65 | default void redirect(RoutingContext context, String newUrl) { 66 | context.response().setStatusCode(HttpResponseStatus.FOUND.code()).putHeader("Location", newUrl); 67 | } 68 | 69 | /** 70 | * 从参数中解析对象类型字段,并判断值是否有效 71 | * @param context 72 | * @return 73 | */ 74 | default String getType(RoutingContext context) { 75 | String type = param(context.request(), "type"); 76 | return QueueTask.isAvailType(type)?type.toLowerCase():null; 77 | } 78 | 79 | /** 80 | * output json 81 | * @param res 82 | * @param json 83 | */ 84 | default void json(HttpServerResponse res, String json) { 85 | res.putHeader("content-type","application/json; charset=utf-8").end(json); 86 | } 87 | 88 | default String param(HttpServerRequest req, String name, String...defValue) { 89 | String val = req.getParam(name); 90 | return (val!=null)?val:(defValue.length>0)?defValue[0]:null; 91 | } 92 | 93 | default int param(HttpServerRequest req, String name, int defValue) { 94 | String val = req.getParam(name); 95 | return NumberUtils.toInt(val, defValue); 96 | } 97 | 98 | default Map params(HttpServerRequest request) { 99 | Map params = new HashMap<>(); 100 | MultiMap mm = request.params(); 101 | for(String name : mm.names()){ 102 | List values = mm.getAll(name); 103 | if(values.size() == 1) 104 | params.put(name, values.get(0)); 105 | else 106 | params.put(name, values.stream().toArray(String[]::new)); 107 | } 108 | return Collections.unmodifiableMap(params); 109 | } 110 | 111 | default void error(HttpServerResponse res, int code, String...msg) { 112 | res.setStatusCode(code); 113 | if(msg != null && msg.length > 0) 114 | res.setStatusMessage(String.join("",msg)); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/query/CodeQuery.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.query; 17 | 18 | import com.gitee.kooder.core.AnalyzerFactory; 19 | import com.gitee.kooder.core.Constants; 20 | import org.apache.lucene.analysis.Analyzer; 21 | import org.apache.lucene.index.Term; 22 | import org.apache.lucene.search.*; 23 | import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper; 24 | import org.apache.lucene.search.spans.SpanNearQuery; 25 | import org.apache.lucene.search.spans.SpanQuery; 26 | import org.apache.lucene.search.spans.SpanTermQuery; 27 | 28 | import java.util.Arrays; 29 | import java.util.List; 30 | 31 | /** 32 | * 源码搜索 33 | * @author Winter Lau (javayou@gmail.com) 34 | */ 35 | public class CodeQuery extends QueryBase { 36 | 37 | /** 38 | * 索引类型 39 | * 40 | * @return 41 | */ 42 | @Override 43 | public String type() { 44 | return Constants.TYPE_CODE; 45 | } 46 | 47 | /** 48 | * 构建查询对象 49 | * @return 50 | */ 51 | @Override 52 | protected Query buildUserQuery() { 53 | return codeQuery(searchKey); 54 | } 55 | 56 | public static Query codeQuery(String q) { 57 | BooleanQuery.Builder builder = new BooleanQuery.Builder(); 58 | 59 | String[] tokens = AnalyzerFactory.getCodeAnalyzer().tokens(q).stream().toArray(String[]::new); 60 | 61 | Query fileNameQuery = createPhraseQuery(Constants.FIELD_FILE_NAME, tokens, 1);//new PhraseQuery(1, Constants.FIELD_FILE_NAME, tokens); 62 | Query sourceQuery = createPhraseQuery(Constants.FIELD_SOURCE, tokens, 5);//new PhraseQuery(5, Constants.FIELD_SOURCE, tokens); 63 | 64 | //make up query 65 | builder.add(new BoostQuery(fileNameQuery, 10.0f), BooleanClause.Occur.SHOULD); 66 | builder.add(sourceQuery, BooleanClause.Occur.SHOULD); 67 | 68 | return builder.setMinimumNumberShouldMatch(1).build(); 69 | } 70 | 71 | /** 72 | * Combine PhraseQuery & WildcardQuery 73 | * @param field 74 | * @param phraseWords 75 | * @param slop 76 | * @return 77 | */ 78 | private static Query createPhraseQuery(String field, String[] phraseWords, int slop) { 79 | if(phraseWords.length == 1) 80 | return new WildcardQuery(new Term(field, phraseWords[0]+"*")); 81 | 82 | SpanQuery[] queryParts = new SpanQuery[phraseWords.length]; 83 | for (int i = 0; i < phraseWords.length; i++) { 84 | if(phraseWords[i].length() == 1) { 85 | queryParts[i] = new SpanTermQuery(new Term(field, phraseWords[i])); 86 | } 87 | else { 88 | WildcardQuery wildQuery = new WildcardQuery(new Term(field, phraseWords[i] + "*")); 89 | queryParts[i] = new SpanMultiTermQueryWrapper<>(wildQuery); 90 | } 91 | } 92 | return new SpanNearQuery(queryParts, slop,true); 93 | } 94 | 95 | /** 96 | * 构建排序对象 97 | * 98 | * @return 99 | */ 100 | @Override 101 | protected Sort buildSort() { 102 | if("update".equals(sort)) 103 | return new Sort(new SortedNumericSortField(Constants.FIELD_LAST_INDEX, SortField.Type.LONG, true)); 104 | return Sort.RELEVANCE; 105 | } 106 | 107 | /** 108 | * list facet names 109 | * @return 110 | */ 111 | @Override 112 | protected List listFacetFields() { 113 | return Arrays.asList(Constants.FIELD_LANGUAGE, Constants.FIELD_REPO_NAME, Constants.FIELD_CODE_OWNER); 114 | } 115 | 116 | /** 117 | * 自定义分词器 118 | * @param forIndex 119 | * @return 120 | */ 121 | @Override 122 | protected Analyzer getAnalyzer(boolean forIndex) { 123 | return AnalyzerFactory.getCodeAnalyzer(); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/core/KooderConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.core; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import java.io.IOException; 22 | import java.nio.file.FileSystemException; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import java.nio.file.Paths; 26 | import java.util.Properties; 27 | 28 | /** 29 | * The global gitee search configuration 30 | * @author Winter Lau 31 | */ 32 | public class KooderConfig { 33 | 34 | private final static Logger log = LoggerFactory.getLogger(KooderConfig.class); 35 | 36 | private final static String CONFIG_NAME = "kooder.properties"; 37 | private static Configuration config; 38 | 39 | static { 40 | try { 41 | config = Configuration.init(CONFIG_NAME); 42 | } catch (IOException e) { 43 | log.error("Failed to loading {}", CONFIG_NAME, e); 44 | } 45 | } 46 | 47 | /** 48 | * 队列配置 49 | * @return 50 | */ 51 | public static Properties getQueueProperties() { 52 | return config.properties("queue"); 53 | } 54 | 55 | /** 56 | * 存储配置 57 | * @return 58 | */ 59 | public static Properties getStoragePropertes() { 60 | return config.properties("storage"); 61 | } 62 | 63 | /** 64 | * HTTP 服务配置 65 | * @return 66 | */ 67 | public static Properties getHttpProperties() { 68 | return config.properties("http"); 69 | } 70 | 71 | /** 72 | * 索引器的配置 73 | * @return 74 | */ 75 | public static Properties getIndexerProperties() { 76 | return config.properties("indexer"); 77 | } 78 | 79 | public static String getHttpBind() { 80 | String bind = config.getProperty("http.bind"); 81 | if(bind != null && bind.trim().length() == 0) 82 | bind = null; 83 | return bind; 84 | } 85 | 86 | public static int getHttpPort() { 87 | return config.getIntProperty("http.port", 8080); 88 | } 89 | 90 | /** 91 | * 解析配置里的路径信息,转成 Path 对象,支持相对路径,绝对路径 92 | * @param spath 93 | * @return 94 | */ 95 | public static Path getPath(String spath) { 96 | return Paths.get(spath).toAbsolutePath().normalize(); 97 | } 98 | 99 | /** 100 | * Check and Initialize directory, if no exists, create it! 101 | * @param spath 102 | * @throws IOException 103 | */ 104 | public static Path checkAndCreatePath(String spath) throws IOException { 105 | return checkAndCreatePath(getPath(spath)); 106 | } 107 | 108 | /** 109 | * Check and Initialize directory, if no exists, create it! 110 | * @param p 111 | * @throws IOException 112 | */ 113 | public static Path checkAndCreatePath(Path p) throws IOException { 114 | //路径已存在,但是不是一个目录,不可读写则报错 115 | if(Files.exists(p) && (!Files.isDirectory(p) || !Files.isReadable(p) || !Files.isWritable(p))) 116 | throw new FileSystemException("Path:" + p + " isn't available."); 117 | //路径不存在,或者不是一个目录,则创建目录 118 | if(!Files.exists(p) || !Files.isDirectory(p)) { 119 | log.warn("Path '" + p + "' for indexes not exists, created it!"); 120 | Files.createDirectory(p); 121 | } 122 | return p; 123 | } 124 | 125 | /*** 126 | * read configuration from kooder.properties 127 | * @param name 128 | * @return 129 | */ 130 | public static String getProperty(String name) { 131 | return config.getProperty(name); 132 | } 133 | 134 | /** 135 | * read configuration from kooder.properties with default value 136 | * @param name 137 | * @param defValue 138 | * @return 139 | */ 140 | public static String getProperty(String name, String defValue) { 141 | return config.getProperty(name, defValue); 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /gateway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | com.gitee.koode 7 | kooder 8 | 1.0-beta4 9 | 10 | 11 | 4.0.0 12 | 13 | kooder-gateway 14 | 15 | 16 | 4.0.0 17 | 18 | 19 | 20 | 21 | 22 | com.gitee.koode 23 | kooder-core 24 | 1.0-beta4 25 | 26 | 27 | 28 | com.gitee.koode 29 | kooder-indexer 30 | 1.0-beta4 31 | 32 | 33 | 34 | io.vertx 35 | vertx-core 36 | ${vertx.version} 37 | 38 | 39 | io.netty 40 | netty-buffer 41 | 42 | 43 | io.netty 44 | netty-codec 45 | 46 | 47 | io.netty 48 | netty-codec-dns 49 | 50 | 51 | io.netty 52 | netty-codec-http2 53 | 54 | 55 | io.netty 56 | netty-codec-http 57 | 58 | 59 | io.netty 60 | netty-codec-socks 61 | 62 | 63 | io.netty 64 | netty-common 65 | 66 | 67 | io.netty 68 | netty-handler 69 | 70 | 71 | io.netty 72 | netty-handler-proxy 73 | 74 | 75 | io.netty 76 | netty-resolver 77 | 78 | 79 | io.netty 80 | netty-resolver-dns 81 | 82 | 83 | io.netty 84 | netty-transport 85 | 86 | 87 | 88 | 89 | io.vertx 90 | vertx-web 91 | ${vertx.version} 92 | 93 | 94 | 95 | io.netty 96 | netty-all 97 | 4.1.54.Final 98 | 99 | 100 | 101 | commons-daemon 102 | commons-daemon 103 | 1.2.3 104 | 105 | 106 | 107 | jline 108 | jline 109 | 2.14.2 110 | 111 | 112 | 113 | org.apache.velocity 114 | velocity-engine-core 115 | 2.2 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/gitee/kooder/server/GatewayBase.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.server; 17 | 18 | import com.gitee.kooder.core.KooderConfig; 19 | import io.vertx.core.Vertx; 20 | import io.vertx.core.VertxOptions; 21 | import io.vertx.core.http.HttpServer; 22 | import io.vertx.core.http.HttpServerRequest; 23 | import io.vertx.core.http.HttpServerResponse; 24 | import io.vertx.ext.web.AllowForwardHeaders; 25 | import io.vertx.ext.web.Router; 26 | import io.vertx.ext.web.RoutingContext; 27 | import org.apache.commons.daemon.Daemon; 28 | import org.apache.commons.daemon.DaemonContext; 29 | import org.apache.commons.lang3.StringUtils; 30 | import org.apache.commons.lang3.math.NumberUtils; 31 | import org.slf4j.Logger; 32 | import org.slf4j.LoggerFactory; 33 | 34 | import java.util.*; 35 | import java.util.stream.Collectors; 36 | 37 | /** 38 | * 把 Gateway 底层逻辑移到 GatewayBase 39 | * @author Winter Lau 40 | */ 41 | public abstract class GatewayBase implements Daemon { 42 | 43 | final static Logger log = LoggerFactory.getLogger("[gateway]"); 44 | 45 | public final static String VERSION = "GSearch/1.0"; 46 | 47 | String bind; 48 | int port; 49 | int workerPoolSize; 50 | private boolean httpLog = true; 51 | 52 | Vertx vertx; 53 | VertxOptions vOptions; 54 | HttpServer server; 55 | Router router; 56 | 57 | public GatewayBase() { 58 | this.bind = KooderConfig.getHttpBind(); 59 | if(StringUtils.isBlank(this.bind)) 60 | this.bind = null; 61 | this.port = KooderConfig.getHttpPort(); 62 | this.workerPoolSize = NumberUtils.toInt(KooderConfig.getProperty("http.worker.pool.size"), Runtime.getRuntime().availableProcessors()); 63 | 64 | this.vOptions = new VertxOptions(); 65 | this.vOptions.setWorkerPoolSize(this.workerPoolSize); 66 | this.vOptions.setBlockedThreadCheckInterval(1000 * 60 * 60); 67 | this.httpLog = !"off".equalsIgnoreCase(KooderConfig.getProperty("http.log")); 68 | } 69 | 70 | @Override 71 | public void init(DaemonContext daemonContext) { 72 | this.vertx = Vertx.vertx(this.vOptions); 73 | this.server = vertx.createHttpServer(); 74 | this.router = Router.router(this.vertx); 75 | router.allowForward(AllowForwardHeaders.X_FORWARD); 76 | //global headers 77 | router.route().handler( context -> { 78 | this.writeGlobalHeaders(context.response()); 79 | context.next(); 80 | }); 81 | } 82 | 83 | @Override 84 | public void stop() { 85 | this.server.close(); 86 | this.vertx.close(); 87 | } 88 | 89 | @Override 90 | public void destroy() { 91 | log.info("EXIT!"); 92 | } 93 | 94 | /** 95 | * 全局 headers 96 | * @param res 97 | */ 98 | protected void writeGlobalHeaders(HttpServerResponse res) { 99 | res.putHeader("server", VERSION); 100 | res.putHeader("date", new Date().toString()); 101 | } 102 | 103 | /** 104 | * 记录日志 105 | * @param context 106 | * @param time 107 | */ 108 | protected void writeAccessLog(RoutingContext context, long time) { 109 | if(httpLog) { 110 | HttpServerRequest req = context.request(); 111 | String ua = req.getHeader("User-Agent"); 112 | if (ua == null) 113 | ua = "-"; 114 | String params = req.params().entries().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(",")); 115 | String msg = String.format("%s - \"%s %s %s %s\" %d %d - %dms - \"%s\"", 116 | req.remoteAddress().hostAddress(), 117 | req.method().name(), 118 | req.uri(), 119 | params, 120 | context.getBodyAsString(), 121 | req.response().getStatusCode(), 122 | req.response().bytesWritten(), 123 | time, 124 | ua); 125 | log.info(msg); 126 | } 127 | } 128 | 129 | } -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/queue/RedisQueueProvider.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.queue; 17 | 18 | import io.lettuce.core.RedisClient; 19 | import io.lettuce.core.RedisURI; 20 | import io.lettuce.core.api.StatefulRedisConnection; 21 | import io.lettuce.core.api.sync.RedisCommands; 22 | import org.apache.commons.lang3.math.NumberUtils; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import java.util.ArrayList; 27 | import java.util.Collection; 28 | import java.util.List; 29 | import java.util.Properties; 30 | 31 | /** 32 | * 使用 Redis 队列 33 | * @author Winter Lau 34 | */ 35 | public class RedisQueueProvider implements QueueProvider { 36 | 37 | private final static Logger log = LoggerFactory.getLogger(RedisQueueProvider.class); 38 | 39 | private String host; 40 | private int port; 41 | private int database; 42 | private String baseKey; 43 | private String username; 44 | private String password; 45 | 46 | private RedisClient client; 47 | 48 | /** 49 | * Connect to redis 50 | * @param props 51 | */ 52 | public RedisQueueProvider(Properties props) { 53 | this.host = props.getProperty("redis.host", "127.0.0.1"); 54 | this.port = NumberUtils.toInt(props.getProperty("redis.port"), 6379); 55 | this.database = NumberUtils.toInt(props.getProperty("redis.database"), 1); 56 | this.baseKey = props.getProperty("redis.key", "gsearch-queue"); 57 | this.username = props.getProperty("username"); 58 | this.password = props.getProperty("password"); 59 | 60 | RedisURI uri = RedisURI.create(host,port); 61 | uri.setDatabase(this.database); 62 | if(password != null) 63 | uri.setPassword(password.toCharArray()); 64 | if(username != null) 65 | uri.setUsername(username); 66 | 67 | this.client = RedisClient.create(uri); 68 | 69 | log.info("Connected to {} at {}}:{}}\n", getRedisVersion(), this.host, this.port); 70 | 71 | } 72 | 73 | private String getRedisVersion() { 74 | try (StatefulRedisConnection connection = client.connect()) { 75 | RedisCommands cmd = connection.sync(); 76 | return cmd.info("redis_version"); 77 | } 78 | } 79 | 80 | @Override 81 | public String name() { 82 | return "redis"; 83 | } 84 | 85 | @Override 86 | public Queue queue(String type) { 87 | return new Queue() { 88 | 89 | private String key = type + '@' + baseKey; 90 | 91 | @Override 92 | public String type() { 93 | return type; 94 | } 95 | 96 | @Override 97 | public void push(Collection tasks) { 98 | try (StatefulRedisConnection connection = client.connect()) { 99 | RedisCommands cmd = connection.sync(); 100 | cmd.rpush(key, tasks.stream().map(t -> t.json()).toArray(String[]::new)); 101 | } 102 | } 103 | 104 | @Override 105 | public List pop(int count) { 106 | String json = null; 107 | List tasks = new ArrayList<>(); 108 | try (StatefulRedisConnection connection = client.connect()) { 109 | RedisCommands cmd = connection.sync(); 110 | do{ 111 | json = cmd.lpop(key); 112 | if(json == null) 113 | break; 114 | QueueTask task = QueueTask.parse(json); 115 | if(task != null) 116 | tasks.add(task); 117 | }while(tasks.size() < count); 118 | } 119 | return tasks; 120 | } 121 | 122 | @Override 123 | public void close() {} 124 | }; 125 | } 126 | 127 | @Override 128 | public void close() { 129 | client.shutdown(); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /indexer/src/main/java/com/gitee/kooder/gitee/Issue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.gitee; 17 | 18 | import com.fasterxml.jackson.annotation.JsonFormat; 19 | import com.gitee.kooder.models.Relation; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Date; 23 | import java.util.List; 24 | import java.util.stream.Collectors; 25 | 26 | /** 27 | * @author zhanggx 28 | */ 29 | public class Issue { 30 | 31 | public static final String STATE_OPEN = "open"; 32 | public static final String STATE_PROGRESSING = "progressing"; 33 | public static final String STATE_CLOSED = "closed"; 34 | public static final String STATE_REJECTED = "rejected"; 35 | 36 | private Integer id; 37 | private String htmlUrl; 38 | private String state; 39 | private String title; 40 | private String body; 41 | private List

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.indexer; 17 | 18 | import org.eclipse.jgit.api.Git; 19 | import org.eclipse.jgit.api.errors.GitAPIException; 20 | import org.eclipse.jgit.lib.*; 21 | import org.eclipse.jgit.revwalk.RevCommit; 22 | import org.eclipse.jgit.treewalk.TreeWalk; 23 | 24 | import java.io.IOException; 25 | import java.nio.file.Path; 26 | import java.nio.file.Paths; 27 | import java.util.List; 28 | 29 | /** 30 | * Build source code indexes for specify repository 31 | * 用于构建某个仓库源码的全量索引,主要是搜索服务的初始化阶段 32 | * @author Winter Lau 33 | */ 34 | public class RepositoryIndexer { 35 | 36 | private Path repoPath; 37 | 38 | public RepositoryIndexer(Path repoPath) { 39 | this.repoPath = repoPath; 40 | } 41 | 42 | public void build() throws IOException, GitAPIException { 43 | try(Git git = Git.open(repoPath.toFile())) { 44 | //remotes 45 | git.remoteList().call().forEach(remote->{ 46 | System.out.printf("%s -> %s\n", remote.getName(), remote.getURIs()); 47 | }); 48 | //tags 49 | List refs = git.tagList().call(); 50 | for(Ref ref : refs) { 51 | //System.out.printf("%s -> %s\n", ref.getName(), ref.getObjectId()); 52 | } 53 | //files 54 | ObjectId lastCommitId = git.getRepository().resolve(Constants.HEAD); 55 | try(TreeWalk allFiles = new TreeWalk(git.getRepository())) { 56 | RevCommit commit = git.getRepository().parseCommit(lastCommitId); 57 | travelTree(git.getRepository(), commit, true); 58 | } 59 | 60 | //commits 61 | Iterable logs = git.log().setMaxCount(10).call(); 62 | for(RevCommit commit : logs) { 63 | //commit related files 64 | TreeWalk tw = new TreeWalk(git.getRepository()); 65 | tw.setRecursive(true); 66 | tw.addTree(commit.getTree()); 67 | 68 | for (RevCommit parent : commit.getParents()) 69 | tw.addTree(parent.getTree()); 70 | 71 | while (tw.next()) { 72 | int similarParents = 0; 73 | for (int i = 1; i < tw.getTreeCount(); i++) 74 | if (tw.getFileMode(i) == tw.getFileMode(0) && tw.getObjectId(0).equals(tw.getObjectId(i))) 75 | similarParents ++; 76 | if (similarParents == 0) { 77 | System.out.printf("%s -> %s : %s\n", 78 | commit.getName(), 79 | commit.getAuthorIdent(), 80 | commit.getShortMessage()); 81 | System.out.printf("\t%s -> depth:%d\n", tw.getPathString(), tw.getDepth()); 82 | } 83 | } 84 | } 85 | } 86 | } 87 | 88 | /** 89 | * 遍历文件 90 | * @param repo 91 | * @param commit 92 | * @param all 93 | * @throws IOException 94 | */ 95 | private static void travelTree(Repository repo, RevCommit commit, boolean all) throws IOException { 96 | // now try to find a specific file 97 | try (TreeWalk treeWalk = new TreeWalk(repo)) { 98 | treeWalk.addTree(commit.getTree()); 99 | treeWalk.setRecursive(true); 100 | while(treeWalk.next()) { 101 | System.out.println(treeWalk.getPathString()); 102 | if("README.md".equals(treeWalk.getNameString())) { 103 | ObjectId objectId = treeWalk.getObjectId(0); 104 | ObjectLoader loader = repo.open(objectId); 105 | // and then one can the loader to read the file 106 | loader.copyTo(System.out); 107 | } 108 | } 109 | } 110 | } 111 | 112 | public static void main(String[] args) throws Exception { 113 | RepositoryIndexer ri = new RepositoryIndexer(Paths.get("D:\\WORKDIR\\J2Cache\\.git")); 114 | long ct = System.currentTimeMillis(); 115 | ri.build(); 116 | System.out.printf("%dms", System.currentTimeMillis()-ct); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/gitee/kooder/server/Gateway.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.server; 17 | 18 | import com.gitee.kooder.core.KooderConfig; 19 | import com.gitee.kooder.file.FileIndexThread; 20 | import com.gitee.kooder.indexer.FetchTaskThread; 21 | import com.gitee.kooder.gitea.GiteaIndexThread; 22 | import com.gitee.kooder.gitee.GiteeIndexThread; 23 | import com.gitee.kooder.indexer.GitlabIndexThread; 24 | import io.vertx.core.http.HttpServerResponse; 25 | import io.vertx.core.net.SocketAddress; 26 | import io.vertx.ext.web.handler.BodyHandler; 27 | import org.apache.commons.lang3.StringUtils; 28 | 29 | import java.net.InetSocketAddress; 30 | import java.util.HashMap; 31 | import java.util.Map; 32 | 33 | /** 34 | * Gateway http server base on vert.x 35 | * @author Winter Lau 36 | */ 37 | public class Gateway extends GatewayBase { 38 | 39 | private final static String pattern_static_file = "/.*\\.(css|ico|js|html|htm|jpg|png|gif|svg)"; 40 | private final static Map startupTasks = new HashMap(){{ 41 | put("indexer", new FetchTaskThread()); 42 | put("gitlab", new GitlabIndexThread()); 43 | put("gitee", new GiteeIndexThread()); 44 | put("gitea", new GiteaIndexThread()); 45 | put("file", new FileIndexThread()); 46 | }}; 47 | 48 | private Gateway() { 49 | super(); 50 | } 51 | 52 | @Override 53 | public void start() { 54 | //body parser 55 | router.route().handler(BodyHandler.create().setHandleFileUploads(false)); 56 | //static files 57 | router.routeWithRegex(pattern_static_file).blockingHandler(new AutoContentTypeStaticHandler(), false); 58 | //action handler 59 | router.route().blockingHandler(context -> { 60 | long ct = System.currentTimeMillis(); 61 | try { 62 | ActionExecutor.execute(context); 63 | } finally { 64 | HttpServerResponse res = context.response(); 65 | if(!res.ended()) 66 | res.end(); 67 | if(!res.closed()) 68 | res.close(); 69 | } 70 | writeAccessLog(context, System.currentTimeMillis() - ct); 71 | }, false); 72 | 73 | InetSocketAddress address = (bind==null)?new InetSocketAddress(this.port):new InetSocketAddress(this.bind, this.port); 74 | 75 | this.server.requestHandler(router).listen(SocketAddress.inetSocketAddress(address)).onSuccess(server -> { 76 | Runtime.getRuntime().addShutdownHook(new Thread(() ->{ 77 | super.stop(); 78 | for(Thread task : startupTasks.values()){ 79 | task.interrupt(); 80 | } 81 | super.destroy(); 82 | })); 83 | log.info("READY ({}:{})!", (this.bind==null)?"*":this.bind, this.port); 84 | }); 85 | 86 | this.startInitTasks(); 87 | } 88 | 89 | /** 90 | * 启动配置文件中指定的初始任务 91 | */ 92 | private void startInitTasks() { 93 | String tasks = KooderConfig.getProperty("http.startup.tasks"); 94 | if(tasks == null) 95 | return ; 96 | String[] taskNames = tasks.split(","); 97 | int tc = 0; 98 | for(String taskName : taskNames) { 99 | if(StringUtils.isNotBlank(taskName)) { 100 | Thread thread = startupTasks.computeIfAbsent(taskName, key -> { 101 | try { 102 | return (Thread) Class.forName(key).getDeclaredConstructor().newInstance(); 103 | } catch (Exception e) { 104 | log.error("Failed to load startup task named: " + taskName, e); 105 | } 106 | return null; 107 | });//.get(taskName); 108 | if (thread != null) { 109 | thread.start(); 110 | tc++; 111 | } 112 | } 113 | } 114 | if(tc > 0) 115 | log.info("Tasks [{}] started.", tasks); 116 | } 117 | 118 | /** 119 | * 启动入口 120 | * @param args 121 | */ 122 | public static void main(String[] args) { 123 | Gateway daemon = new Gateway(); 124 | daemon.init(null); 125 | daemon.start(); 126 | } 127 | 128 | } -------------------------------------------------------------------------------- /core/src/main/java/com/gitee/kooder/core/Constants.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021, OSChina (oschina.net@gmail.com). 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.gitee.kooder.core; 17 | 18 | /** 19 | * 搜索相关常量定义 20 | * @author Winter Lau 21 | */ 22 | public interface Constants { 23 | 24 | String PRODUCT_NAME = "RedCode"; //产品名称 25 | 26 | String EMPTYSTRING = ""; 27 | String EMPTYJSON = "{}"; 28 | 29 | int VISIBILITY_PRIVATE = 0; 30 | int VISIBILITY_INTERNAL = 1; 31 | int VISIBILITY_PUBLIC = 2; 32 | 33 | String TYPE_REPOSITORY = "repo"; //仓库 34 | String TYPE_ISSUE = "issue"; //Issue 35 | String TYPE_PR = "pr"; //Pull Requests 36 | String TYPE_COMMIT = "commit"; //Commits 37 | String TYPE_WIKI = "wiki"; //WIKI 38 | String TYPE_CODE = "code"; //Source Code 39 | String TYPE_USER = "user"; //Users 40 | String TYPE_METADATA = "_metadata"; 41 | 42 | byte RECOMM_NONE = 0x00; //未被推荐项目(推荐级别定义必须递增) 43 | byte RECOMM = 0x01; //推荐项目 44 | byte RECOMM_GVP = 0x02; //GVP推荐项目 45 | 46 | byte REPO_BLOCK_YES = 0x01; 47 | byte REPO_BLOCK_NO = 0x00; 48 | 49 | byte REPO_FORK_NO = 0x00; 50 | byte REPO_FORK_YES = 0x01; 51 | 52 | byte ISSUE_PUBLIC = 1; 53 | 54 | String FIELD_OBJECTS = "objects"; //json 数据中的对象数字字段名称 55 | 56 | String FACET_VALUE_EMPTY = "Unknown"; //空 Facet 字段对应的默认值 57 | 58 | /* 文档字段定义 */ 59 | String FIELD_LICENSE = "license"; 60 | String FIELD_ID = "id"; 61 | String FIELD_IDENT = "ident"; 62 | 63 | /* 代码相关的字段定义 */ 64 | String FIELD_VENDER = "vender"; 65 | String FIELD_UUID = "uuid"; 66 | String FIELD_LANGUAGE = "lang"; 67 | String FIELD_NAME = "name"; 68 | String FIELD_TITLE = "title"; 69 | String FIELD_DISPLAY_NAME = "display.name"; 70 | String FIELD_DESC = "desc"; 71 | String FIELD_URL = "url"; 72 | String FIELD_README = "readme"; 73 | String FIELD_FORK = "fork"; 74 | String FIELD_CREATED_AT = "createdAt"; 75 | String FIELD_UPDATED_AT = "updatedAt"; 76 | String FIELD_CLOSED_AT = "closedAt"; 77 | String FIELD_STAR_COUNT = "starCount"; 78 | String FIELD_FORK_COUNT = "forkCount"; 79 | String FIELD_G_INDEX = "gindex"; 80 | String FIELD_TAGS = "tags"; 81 | String FIELD_CATALOGS = "catalogs"; 82 | String FIELD_BRANCH = "branch"; 83 | 84 | String FIELD_REPO_ID = "repo.id"; 85 | String FIELD_REPO_NAME = "repo.name"; 86 | String FIELD_REPO_PATH = "repo.path"; 87 | String FIELD_REPO_URL = "repo.url"; 88 | 89 | String FIELD_FILE_NAME = "file.name"; 90 | String FIELD_FILE_LOCATION = "file.location"; 91 | 92 | //Gitee Enterprise 93 | String FIELD_ENTERPRISE_ID = "e.id"; 94 | String FIELD_ENTERPRISE_NAME = "e.name"; 95 | String FIELD_ENTERPRISE_URL = "e.url"; 96 | 97 | //Gitee Enterprise Program 98 | String FIELD_PROGRAM_ID = "p.id"; 99 | String FIELD_PROGRAM_NAME = "p.name"; 100 | String FIELD_PROGRAM_URL = "p.url"; 101 | 102 | //User 103 | String FIELD_USER_ID = "u.id"; 104 | String FIELD_USER_NAME = "u.name"; 105 | String FIELD_USER_URL = "u.url"; 106 | 107 | String FIELD_CODE_OWNER = "owner"; 108 | String FIELD_FILE_HASH = "file.hash"; 109 | String FIELD_SOURCE = "source"; 110 | 111 | String FIELD_RECOMM = "recomm"; 112 | String FIELD_BLOCK = "block"; 113 | String FIELD_VISIBILITY = "visibility"; 114 | String FIELD_LAST_INDEX = "modified"; 115 | String FIELD_REVISION = "revision"; 116 | String FIELD_SCM = "scm"; 117 | String FIELD_STATUS = "status"; 118 | String FIELD_TIMESTAMP = "timestamp"; 119 | 120 | String FIELD_LINES_TOTAL = "lines.total"; 121 | String FIELD_LINES_CODE = "lines.code"; 122 | String FIELD_LINES_BLANK = "lines.blank"; 123 | String FIELD_LINES_COMMENT = "lines.comment"; 124 | String FIELD_COMPLEXITY = "complexity"; 125 | 126 | String DEFAULT_SECRET_TOKEN = "gsearch"; 127 | 128 | String GITLAB = "gitlab"; 129 | String GITEE = "gitee"; 130 | String GITEA = "gitea"; 131 | } 132 | --------------------------------------------------------------------------------