diff --git a/src/main/scala/gitbucket/core/controller/AccountController.scala b/src/main/scala/gitbucket/core/controller/AccountController.scala index 65212e3..54bdc58 100644 --- a/src/main/scala/gitbucket/core/controller/AccountController.scala +++ b/src/main/scala/gitbucket/core/controller/AccountController.scala @@ -133,7 +133,7 @@ val members = getGroupMembers(account.userName) gitbucket.core.account.html.repositories(account, if(account.isGroupAccount) Nil else getGroupsByUserName(userName), - getVisibleRepositories(context.loginAccount, context.baseUrl, Some(userName)), + getVisibleRepositories(context.loginAccount, Some(userName)), context.loginAccount.exists(x => members.exists { member => member.userName == x.userName && member.isManager })) } } @@ -366,7 +366,7 @@ */ post("/new", newRepositoryForm)(usersOnly { form => LockUtil.lock(s"${form.owner}/${form.name}"){ - if(getRepository(form.owner, form.name, context.baseUrl).isEmpty){ + if(getRepository(form.owner, form.name).isEmpty){ createRepository(form.owner, form.name, form.description, form.isPrivate, form.createReadme) } @@ -385,9 +385,9 @@ data <- extractFromJsonBody[CreateARepository] if data.isValid } yield { LockUtil.lock(s"${owner}/${data.name}") { - if(getRepository(owner, data.name, context.baseUrl).isEmpty){ + if(getRepository(owner, data.name).isEmpty){ createRepository(owner, data.name, data.description, data.`private`, data.auto_init) - val repository = getRepository(owner, data.name, context.baseUrl).get + val repository = getRepository(owner, data.name).get JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(owner).get))) } else { ApiError( @@ -409,9 +409,9 @@ data <- extractFromJsonBody[CreateARepository] if data.isValid } yield { LockUtil.lock(s"${groupName}/${data.name}") { - if(getRepository(groupName, data.name, context.baseUrl).isEmpty){ + if(getRepository(groupName, data.name).isEmpty){ createRepository(groupName, data.name, data.description, data.`private`, data.auto_init) - val repository = getRepository(groupName, data.name, context.baseUrl).get + val repository = getRepository(groupName, data.name).get JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(groupName).get))) } else { ApiError( @@ -447,7 +447,7 @@ val accountName = form.accountName LockUtil.lock(s"${accountName}/${repository.name}"){ - if(getRepository(accountName, repository.name, baseUrl).isDefined || + if(getRepository(accountName, repository.name).isDefined || (accountName != loginUserName && !getGroupsByUserName(loginUserName).contains(accountName))){ // redirect to the repository if repository already exists redirect(s"/${accountName}/${repository.name}") diff --git a/src/main/scala/gitbucket/core/controller/ControllerBase.scala b/src/main/scala/gitbucket/core/controller/ControllerBase.scala index 5d449ba..c8dabcc 100644 --- a/src/main/scala/gitbucket/core/controller/ControllerBase.scala +++ b/src/main/scala/gitbucket/core/controller/ControllerBase.scala @@ -3,6 +3,7 @@ import gitbucket.core.api.ApiError import gitbucket.core.model.Account import gitbucket.core.service.{AccountService, SystemSettingsService} +import gitbucket.core.service.RepositoryService.{RepositoryInfo, RepositoryUrls} import gitbucket.core.util.ControlUtil._ import gitbucket.core.util.Directory._ import gitbucket.core.util.Implicits._ @@ -180,11 +181,12 @@ * Context object for the current request. */ case class Context(settings: SystemSettingsService.SystemSettings, loginAccount: Option[Account], request: HttpServletRequest){ - val path = settings.baseUrl.getOrElse(request.getContextPath) val currentPath = request.getRequestURI.substring(request.getContextPath.length) val baseUrl = settings.baseUrl(request) val host = new java.net.URL(baseUrl).getHost + val urls = (repositoryInfo:RepositoryInfo) => new RepositoryUrls(baseUrl, settings.sshAddress, repositoryInfo.owner, repositoryInfo.name) + val wikiUrls = (repositoryInfo:RepositoryInfo) => new RepositoryUrls(baseUrl, settings.sshAddress, repositoryInfo.owner, repositoryInfo.name + ".wiki") val platform = request.getHeader("User-Agent") match { case null => null case agent if agent.contains("Mac") => "mac" diff --git a/src/main/scala/gitbucket/core/controller/DashboardController.scala b/src/main/scala/gitbucket/core/controller/DashboardController.scala index 713e9c0..1516c0f 100644 --- a/src/main/scala/gitbucket/core/controller/DashboardController.scala +++ b/src/main/scala/gitbucket/core/controller/DashboardController.scala @@ -94,7 +94,7 @@ val userName = context.loginAccount.get.userName val condition = getOrCreateCondition(Keys.Session.DashboardIssues, filter, userName) - val userRepos = getUserRepositories(userName, context.baseUrl, true).map(repo => repo.owner -> repo.name) + val userRepos = getUserRepositories(userName, true).map(repo => repo.owner -> repo.name) val page = IssueSearchCondition.page(request) html.issues( diff --git a/src/main/scala/gitbucket/core/controller/IndexController.scala b/src/main/scala/gitbucket/core/controller/IndexController.scala index f264131..6a804f8 100644 --- a/src/main/scala/gitbucket/core/controller/IndexController.scala +++ b/src/main/scala/gitbucket/core/controller/IndexController.scala @@ -29,8 +29,8 @@ val loginAccount = context.loginAccount if(loginAccount.isEmpty) { html.index(getRecentActivities(), - getVisibleRepositories(loginAccount, context.baseUrl, withoutPhysicalInfo = true), - loginAccount.map{ account => getUserRepositories(account.userName, context.baseUrl, withoutPhysicalInfo = true) }.getOrElse(Nil) + getVisibleRepositories(loginAccount, withoutPhysicalInfo = true), + loginAccount.map{ account => getUserRepositories(account.userName, withoutPhysicalInfo = true) }.getOrElse(Nil) ) } else { val loginUserName = loginAccount.get.userName @@ -40,8 +40,8 @@ visibleOwnerSet ++= loginUserGroups html.index(getRecentActivitiesByOwners(visibleOwnerSet), - getVisibleRepositories(loginAccount, context.baseUrl, withoutPhysicalInfo = true), - loginAccount.map{ account => getUserRepositories(account.userName, context.baseUrl, withoutPhysicalInfo = true) }.getOrElse(Nil) + getVisibleRepositories(loginAccount, withoutPhysicalInfo = true), + loginAccount.map{ account => getUserRepositories(account.userName, withoutPhysicalInfo = true) }.getOrElse(Nil) ) } } diff --git a/src/main/scala/gitbucket/core/controller/PullRequestsController.scala b/src/main/scala/gitbucket/core/controller/PullRequestsController.scala index 7a95c42..11141ff 100644 --- a/src/main/scala/gitbucket/core/controller/PullRequestsController.scala +++ b/src/main/scala/gitbucket/core/controller/PullRequestsController.scala @@ -137,7 +137,7 @@ baseOwner <- users.get(repository.owner) headOwner <- users.get(pullRequest.requestUserName) issueUser <- users.get(issue.openedUserName) - headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName, baseUrl) + headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName) } yield { JsonFormat(ApiPullRequest( issue, @@ -196,7 +196,7 @@ issue, pullreq, repository, - getRepository(pullreq.requestUserName, pullreq.requestRepositoryName, context.baseUrl).get) + getRepository(pullreq.requestUserName, pullreq.requestRepositoryName).get) } } getOrElse NotFound }) @@ -229,7 +229,7 @@ if(branchProtection.needStatusCheck(loginAccount.userName)){ flash += "error" -> s"branch ${pullreq.requestBranch} is protected need status check." } else { - val repository = getRepository(owner, name, context.baseUrl).get + val repository = getRepository(owner, name).get LockUtil.lock(s"${owner}/${name}"){ val alias = if(pullreq.repositoryName == pullreq.requestRepositoryName && pullreq.userName == pullreq.requestUserName){ pullreq.branch @@ -310,7 +310,7 @@ pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.commitIdTo) // close issue by content of pull request - val defaultBranch = getRepository(owner, name, context.baseUrl).get.repository.defaultBranch + val defaultBranch = getRepository(owner, name).get.repository.defaultBranch if(pullreq.branch == defaultBranch){ commits.flatten.foreach { commit => closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, owner, name) @@ -343,7 +343,7 @@ val headBranch:Option[String] = params.get("head") (forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match { case (Some(originUserName), Some(originRepositoryName)) => { - getRepository(originUserName, originRepositoryName, context.baseUrl).map { originRepository => + getRepository(originUserName, originRepositoryName).map { originRepository => using( Git.open(getRepositoryDir(originUserName, originRepositoryName)), Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name)) @@ -384,12 +384,12 @@ forkedRepository.repository.originRepositoryName } else { // Sibling repository - getUserRepositories(originOwner, context.baseUrl).find { x => + getUserRepositories(originOwner).find { x => x.repository.originUserName == forkedRepository.repository.originUserName && x.repository.originRepositoryName == forkedRepository.repository.originRepositoryName }.map(_.repository.repositoryName) }; - originRepository <- getRepository(originOwner, originRepositoryName, context.baseUrl) + originRepository <- getRepository(originOwner, originRepositoryName) ) yield { using( Git.open(getRepositoryDir(originRepository.owner, originRepository.name)), @@ -457,7 +457,7 @@ getForkedRepositories(forkedRepository.owner, forkedRepository.name).find(_._1 == originOwner).map(_._2) } }; - originRepository <- getRepository(originOwner, originRepositoryName, context.baseUrl) + originRepository <- getRepository(originOwner, originRepositoryName) ) yield { using( Git.open(getRepositoryDir(originRepository.owner, originRepository.name)), diff --git a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala index 4abf09c..372e002 100644 --- a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala +++ b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala @@ -560,8 +560,7 @@ html.forked( getRepository( repository.repository.originUserName.getOrElse(repository.owner), - repository.repository.originRepositoryName.getOrElse(repository.name), - context.baseUrl), + repository.repository.originRepositoryName.getOrElse(repository.name)), getForkedRepositories( repository.repository.originUserName.getOrElse(repository.owner), repository.repository.originRepositoryName.getOrElse(repository.name)), diff --git a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala index 01999f6..bb81092 100644 --- a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala +++ b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala @@ -23,6 +23,7 @@ "notification" -> trim(label("Notification", boolean())), "activityLogLimit" -> trim(label("Limit of activity logs", optional(number()))), "ssh" -> trim(label("SSH access", boolean())), + "sshHost" -> trim(label("SSH host", optional(text()))), "sshPort" -> trim(label("SSH port", optional(number()))), "useSMTP" -> trim(label("SMTP", boolean())), "smtp" -> optionalIfNotChecked("useSMTP", mapping( @@ -50,9 +51,14 @@ "keystore" -> trim(label("Keystore", optional(text()))) )(Ldap.apply)) )(SystemSettings.apply).verifying { settings => - if(settings.ssh && settings.baseUrl.isEmpty){ - Seq("baseUrl" -> "Base URL is required if SSH access is enabled.") - } else Nil + Vector( + if(settings.ssh && settings.baseUrl.isEmpty){ + Some("baseUrl" -> "Base URL is required if SSH access is enabled.") + } else None, + if(settings.ssh && settings.sshHost.isEmpty){ + Some("sshHost" -> "SSH host is required if SSH access is enabled.") + } else None + ).flatten } private val pluginForm = mapping( @@ -68,16 +74,13 @@ post("/admin/system", form)(adminOnly { form => saveSystemSettings(form) - if(form.ssh && SshServer.isActive && context.settings.sshPort != form.sshPort){ + if (form.sshAddress != context.settings.sshAddress) { SshServer.stop() - } - - if(form.ssh && !SshServer.isActive && form.baseUrl.isDefined){ - SshServer.start( - form.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), - form.baseUrl.get) - } else if(!form.ssh && SshServer.isActive){ - SshServer.stop() + for { + sshAddress <- form.sshAddress + baseUrl <- form.baseUrl + } + SshServer.start(sshAddress, baseUrl) } flash += "info" -> "System settings has been updated." diff --git a/src/main/scala/gitbucket/core/service/RepositoryService.scala b/src/main/scala/gitbucket/core/service/RepositoryService.scala index 8e74642..2544876 100644 --- a/src/main/scala/gitbucket/core/service/RepositoryService.scala +++ b/src/main/scala/gitbucket/core/service/RepositoryService.scala @@ -1,5 +1,6 @@ package gitbucket.core.service +import gitbucket.core.service.SystemSettingsService.SshAddress import gitbucket.core.model.{Collaborator, Repository, Account} import gitbucket.core.model.Profile._ import gitbucket.core.util.JGitUtil @@ -194,10 +195,9 @@ * * @param userName the user name of the repository owner * @param repositoryName the repository name - * @param baseUrl the base url of this application * @return the repository information */ - def getRepository(userName: String, repositoryName: String, baseUrl: String)(implicit s: Session): Option[RepositoryInfo] = { + def getRepository(userName: String, repositoryName: String)(implicit s: Session): Option[RepositoryInfo] = { (Repositories filter { t => t.byRepository(userName, repositoryName) } firstOption) map { repository => // for getting issue count and pull request count val issues = Issues.filter { t => @@ -205,7 +205,7 @@ }.map(_.pullRequest).list new RepositoryInfo( - JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl), + JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName), repository, issues.count(_ == false), issues.count(_ == true), @@ -234,7 +234,7 @@ }.list } - def getUserRepositories(userName: String, baseUrl: String, withoutPhysicalInfo: Boolean = false) + def getUserRepositories(userName: String, withoutPhysicalInfo: Boolean = false) (implicit s: Session): List[RepositoryInfo] = { Repositories.filter { t1 => (t1.userName === userName.bind) || @@ -242,9 +242,9 @@ }.sortBy(_.lastActivityDate desc).list.map{ repository => new RepositoryInfo( if(withoutPhysicalInfo){ - new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName, baseUrl) + new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName) } else { - JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl) + JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName) }, repository, getForkedCount( @@ -260,13 +260,12 @@ * If repositoryUserName is given then filters results by repository owner. * * @param loginAccount the logged in account - * @param baseUrl the base url of this application * @param repositoryUserName the repository owner (if None then returns all repositories which are visible for logged in user) * @param withoutPhysicalInfo if true then the result does not include physical repository information such as commit count, * branches and tags * @return the repository information which is sorted in descending order of lastActivityDate. */ - def getVisibleRepositories(loginAccount: Option[Account], baseUrl: String, repositoryUserName: Option[String] = None, + def getVisibleRepositories(loginAccount: Option[Account], repositoryUserName: Option[String] = None, withoutPhysicalInfo: Boolean = false) (implicit s: Session): List[RepositoryInfo] = { (loginAccount match { @@ -284,9 +283,9 @@ }.sortBy(_.lastActivityDate desc).list.map{ repository => new RepositoryInfo( if(withoutPhysicalInfo){ - new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName, baseUrl) + new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName) } else { - JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl) + JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName) }, repository, getForkedCount( @@ -389,31 +388,45 @@ object RepositoryService { - case class RepositoryInfo(owner: String, name: String, httpUrl: String, repository: Repository, + case class RepositoryInfo(owner: String, name: String, repository: Repository, issueCount: Int, pullCount: Int, commitCount: Int, forkedCount: Int, - branchList: Seq[String], tags: Seq[JGitUtil.TagInfo], managers: Seq[String]){ - - lazy val host = """^https?://(.+?)(:\d+)?/""".r.findFirstMatchIn(httpUrl).get.group(1) - - def sshUrl(port: Int, userName: String) = s"ssh://${userName}@${host}:${port}/${owner}/${name}.git" - - def sshOpenRepoUrl(platform: String, port: Int, userName: String) = openRepoUrl(platform, sshUrl(port, userName)) - - def httpOpenRepoUrl(platform: String) = openRepoUrl(platform, httpUrl) - - def openRepoUrl(platform: String, openUrl: String) = s"github-${platform}://openRepo/${openUrl}" + branchList: Seq[String], tags: Seq[JGitUtil.TagInfo], managers: Seq[String]) { /** * Creates instance with issue count and pull request count. */ def this(repo: JGitUtil.RepositoryInfo, model: Repository, issueCount: Int, pullCount: Int, forkedCount: Int, managers: Seq[String]) = - this(repo.owner, repo.name, repo.url, model, issueCount, pullCount, repo.commitCount, forkedCount, repo.branchList, repo.tags, managers) + this( + repo.owner, repo.name, model, + issueCount, pullCount, + repo.commitCount, forkedCount, repo.branchList, repo.tags, managers) /** * Creates instance without issue count and pull request count. */ - def this(repo: JGitUtil.RepositoryInfo, model: Repository, forkedCount: Int, managers: Seq[String]) = - this(repo.owner, repo.name, repo.url, model, 0, 0, repo.commitCount, forkedCount, repo.branchList, repo.tags, managers) + def this(repo: JGitUtil.RepositoryInfo, model: Repository, forkedCount: Int, managers: Seq[String]) = + this( + repo.owner, repo.name, model, + 0, 0, + repo.commitCount, forkedCount, repo.branchList, repo.tags, managers) + } + + final class RepositoryUrls(baseUrl:String, sshAddress:Option[SshAddress], owner:String, name:String) { + def httpUrl:String = + s"${baseUrl}/git/${owner}/${name}.git" + + // BETTER make this return an Option and use it in the gui + def sshUrl(userName: String):String = + sshAddress.fold("")(adr => s"ssh://${userName}@${adr.host}:${adr.port}/${owner}/${name}.git") + + def sshOpenRepoUrl(platform: String, userName: String) = + openRepoUrl(platform, sshUrl(userName)) + + def httpOpenRepoUrl(platform: String) = + openRepoUrl(platform, httpUrl) + + private def openRepoUrl(platform: String, openUrl: String) = + s"github-${platform}://openRepo/${openUrl}" } case class RepositoryTreeNode(owner: String, name: String, children: List[RepositoryTreeNode]) diff --git a/src/main/scala/gitbucket/core/service/SystemSettingsService.scala b/src/main/scala/gitbucket/core/service/SystemSettingsService.scala index 3346bec..e5096ba 100644 --- a/src/main/scala/gitbucket/core/service/SystemSettingsService.scala +++ b/src/main/scala/gitbucket/core/service/SystemSettingsService.scala @@ -1,6 +1,7 @@ package gitbucket.core.service import gitbucket.core.util.{Directory, ControlUtil} +import gitbucket.core.util.Implicits._ import Directory._ import ControlUtil._ import SystemSettingsService._ @@ -21,6 +22,7 @@ props.setProperty(Notification, settings.notification.toString) settings.activityLogLimit.foreach(x => props.setProperty(ActivityLogLimit, x.toString)) props.setProperty(Ssh, settings.ssh.toString) + settings.sshHost.foreach(x => props.setProperty(SshHost, x.trim)) settings.sshPort.foreach(x => props.setProperty(SshPort, x.toString)) props.setProperty(UseSMTP, settings.useSMTP.toString) if(settings.useSMTP) { @@ -75,6 +77,7 @@ getValue(props, Notification, false), getOptionValue[Int](props, ActivityLogLimit, None), getValue(props, Ssh, false), + getOptionValue[String](props, SshHost, None).map(_.trim), getOptionValue(props, SshPort, Some(DefaultSshPort)), getValue(props, UseSMTP, getValue(props, Notification, false)), // handle migration scenario from only notification to useSMTP if(getValue(props, UseSMTP, getValue(props, Notification, false))){ @@ -126,16 +129,20 @@ notification: Boolean, activityLogLimit: Option[Int], ssh: Boolean, + sshHost: Option[String], sshPort: Option[Int], useSMTP: Boolean, smtp: Option[Smtp], ldapAuthentication: Boolean, ldap: Option[Ldap]){ - def baseUrl(request: HttpServletRequest): String = baseUrl.getOrElse { - defining(request.getRequestURL.toString){ url => - url.substring(0, url.length - (request.getRequestURI.length - request.getContextPath.length)) + def baseUrl(request: HttpServletRequest): String = baseUrl.fold(request.baseUrl)(_.stripSuffix("/")) + + def sshAddress:Option[SshAddress] = + for { + host <- sshHost + if ssh } - }.stripSuffix("/") + yield SshAddress(host, sshPort.getOrElse(DefaultSshPort)) } case class Ldap( @@ -161,6 +168,10 @@ fromAddress: Option[String], fromName: Option[String]) + case class SshAddress( + host:String, + port:Int) + val DefaultSshPort = 29418 val DefaultSmtpPort = 25 val DefaultLdapPort = 389 @@ -174,6 +185,7 @@ private val Notification = "notification" private val ActivityLogLimit = "activity_log_limit" private val Ssh = "ssh" + private val SshHost = "ssh.host" private val SshPort = "ssh.port" private val UseSMTP = "useSMTP" private val SmtpHost = "smtp.host" diff --git a/src/main/scala/gitbucket/core/service/WebHookService.scala b/src/main/scala/gitbucket/core/service/WebHookService.scala index 059ca5d..d04d90b 100644 --- a/src/main/scala/gitbucket/core/service/WebHookService.scala +++ b/src/main/scala/gitbucket/core/service/WebHookService.scala @@ -161,7 +161,7 @@ baseOwner <- users.get(repository.owner) headOwner <- users.get(pullRequest.requestUserName) issueUser <- users.get(issue.openedUserName) - headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName, baseUrl) + headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName) } yield { WebHookPullRequestPayload( action = action, @@ -200,7 +200,7 @@ import WebHookService._ for{ ((issue, issueUser, pullRequest, baseOwner, headOwner), webHooks) <- getPullRequestsByRequestForWebhook(requestRepository.owner, requestRepository.name, requestBranch) - baseRepo <- getRepository(pullRequest.userName, pullRequest.repositoryName, baseUrl) + baseRepo <- getRepository(pullRequest.userName, pullRequest.repositoryName) } yield { val payload = WebHookPullRequestPayload( action = action, @@ -229,7 +229,7 @@ baseOwner <- users.get(repository.owner) headOwner <- users.get(pullRequest.requestUserName) issueUser <- users.get(issue.openedUserName) - headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName, baseUrl) + headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName) } yield { WebHookPullRequestReviewCommentPayload( action = action, diff --git a/src/main/scala/gitbucket/core/service/WikiService.scala b/src/main/scala/gitbucket/core/service/WikiService.scala index 29d8e56..f1d7749 100644 --- a/src/main/scala/gitbucket/core/service/WikiService.scala +++ b/src/main/scala/gitbucket/core/service/WikiService.scala @@ -1,6 +1,7 @@ package gitbucket.core.service import java.util.Date +import gitbucket.core.service.SystemSettingsService.SshAddress import gitbucket.core.model.Account import gitbucket.core.util._ import gitbucket.core.util.ControlUtil._ @@ -14,6 +15,7 @@ import org.eclipse.jgit.api.errors.PatchFormatException import scala.collection.JavaConverters._ import RepositoryService.RepositoryInfo +import RepositoryService.RepositoryUrls object WikiService { @@ -37,11 +39,6 @@ * @param date the commit date */ case class WikiPageHistoryInfo(name: String, committer: String, message: String, date: Date) - - def httpUrl(repository: RepositoryInfo) = repository.httpUrl.replaceFirst("\\.git\\Z", ".wiki.git") - - def sshUrl(repository: RepositoryInfo, settings: SystemSettingsService.SystemSettings, userName: String) = - repository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), userName).replaceFirst("\\.git\\Z", ".wiki.git") } trait WikiService { diff --git a/src/main/scala/gitbucket/core/servlet/BasicAuthenticationFilter.scala b/src/main/scala/gitbucket/core/servlet/BasicAuthenticationFilter.scala index 421a461..696f5f1 100644 --- a/src/main/scala/gitbucket/core/servlet/BasicAuthenticationFilter.scala +++ b/src/main/scala/gitbucket/core/servlet/BasicAuthenticationFilter.scala @@ -74,7 +74,7 @@ request.paths match { case Array(_, repositoryOwner, repositoryName, _*) => - getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", ""), "") match { + getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", "")) match { case Some(repository) => { if(!isUpdating && !repository.repository.isPrivate && settings.allowAnonymousAccess){ chain.doFilter(request, response) diff --git a/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala b/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala index 1f8347a..0583f61 100644 --- a/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala +++ b/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala @@ -160,7 +160,7 @@ countIssue(IssueSearchCondition(state = "open"), false, owner -> repository) + countIssue(IssueSearchCondition(state = "closed"), false, owner -> repository) - val repositoryInfo = getRepository(owner, repository, baseUrl).get + val repositoryInfo = getRepository(owner, repository).get // Extract new commit and apply issue comment val defaultBranch = repositoryInfo.repository.defaultBranch diff --git a/src/main/scala/gitbucket/core/ssh/GitCommand.scala b/src/main/scala/gitbucket/core/ssh/GitCommand.scala index 8e19255..eb12001 100644 --- a/src/main/scala/gitbucket/core/ssh/GitCommand.scala +++ b/src/main/scala/gitbucket/core/ssh/GitCommand.scala @@ -87,11 +87,11 @@ } -class DefaultGitUploadPack(owner: String, repoName: String, baseUrl: String) extends DefaultGitCommand(owner, repoName) +class DefaultGitUploadPack(owner: String, repoName: String) extends DefaultGitCommand(owner, repoName) with RepositoryService with AccountService { override protected def runTask(user: String)(implicit session: Session): Unit = { - getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", ""), baseUrl).foreach { repositoryInfo => + getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", "")).foreach { repositoryInfo => if(!repositoryInfo.repository.isPrivate || isWritableUser(user, repositoryInfo)){ using(Git.open(getRepositoryDir(owner, repoName))) { git => val repository = git.getRepository @@ -107,7 +107,7 @@ with RepositoryService with AccountService { override protected def runTask(user: String)(implicit session: Session): Unit = { - getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", ""), baseUrl).foreach { repositoryInfo => + getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", "")).foreach { repositoryInfo => if(isWritableUser(user, repositoryInfo)){ using(Git.open(getRepositoryDir(owner, repoName))) { git => val repository = git.getRepository @@ -124,7 +124,7 @@ } } -class PluginGitUploadPack(repoName: String, baseUrl: String, routing: GitRepositoryRouting) extends GitCommand +class PluginGitUploadPack(repoName: String, routing: GitRepositoryRouting) extends GitCommand with SystemSettingsService { override protected def runTask(user: String)(implicit session: Session): Unit = { @@ -139,7 +139,7 @@ } } -class PluginGitReceivePack(repoName: String, baseUrl: String, routing: GitRepositoryRouting) extends GitCommand +class PluginGitReceivePack(repoName: String, routing: GitRepositoryRouting) extends GitCommand with SystemSettingsService { override protected def runTask(user: String)(implicit session: Session): Unit = { @@ -163,9 +163,9 @@ logger.debug(s"command: $command") command match { - case SimpleCommandRegex ("upload" , repoName) if(pluginRepository(repoName)) => new PluginGitUploadPack (repoName, baseUrl, routing(repoName)) - case SimpleCommandRegex ("receive", repoName) if(pluginRepository(repoName)) => new PluginGitReceivePack(repoName, baseUrl, routing(repoName)) - case DefaultCommandRegex("upload" , owner, repoName) => new DefaultGitUploadPack (owner, repoName, baseUrl) + case SimpleCommandRegex ("upload" , repoName) if(pluginRepository(repoName)) => new PluginGitUploadPack (repoName, routing(repoName)) + case SimpleCommandRegex ("receive", repoName) if(pluginRepository(repoName)) => new PluginGitReceivePack(repoName, routing(repoName)) + case DefaultCommandRegex("upload" , owner, repoName) => new DefaultGitUploadPack (owner, repoName) case DefaultCommandRegex("receive", owner, repoName) => new DefaultGitReceivePack(owner, repoName, baseUrl) case _ => new UnknownCommand(command) } diff --git a/src/main/scala/gitbucket/core/ssh/NoShell.scala b/src/main/scala/gitbucket/core/ssh/NoShell.scala index bd30ccf..6f2f42d 100644 --- a/src/main/scala/gitbucket/core/ssh/NoShell.scala +++ b/src/main/scala/gitbucket/core/ssh/NoShell.scala @@ -1,12 +1,13 @@ package gitbucket.core.ssh import gitbucket.core.service.SystemSettingsService +import gitbucket.core.service.SystemSettingsService.SshAddress import org.apache.sshd.common.Factory import org.apache.sshd.server.{Environment, ExitCallback, Command} import java.io.{OutputStream, InputStream} import org.eclipse.jgit.lib.Constants -class NoShell extends Factory[Command] with SystemSettingsService { +class NoShell(sshAddress:SshAddress) extends Factory[Command] { override def create(): Command = new Command() { private var in: InputStream = null private var out: OutputStream = null @@ -15,7 +16,6 @@ override def start(env: Environment): Unit = { val user = env.getEnv.get("USER") - val port = loadSystemSettings().sshPort.getOrElse(SystemSettingsService.DefaultSshPort) val message = """ | Welcome to @@ -31,8 +31,8 @@ | | Please use: | - | git clone ssh://%s@GITBUCKET_HOST:%d/OWNER/REPOSITORY_NAME.git - """.stripMargin.format(user, port).replace("\n", "\r\n") + "\r\n" + | git clone ssh://%s@%s:%d/OWNER/REPOSITORY_NAME.git + """.stripMargin.format(user, sshAddress.host, sshAddress.port).replace("\n", "\r\n") + "\r\n" err.write(Constants.encode(message)) err.flush() in.close() diff --git a/src/main/scala/gitbucket/core/ssh/SshServerListener.scala b/src/main/scala/gitbucket/core/ssh/SshServerListener.scala index f79ae93..8795e40 100644 --- a/src/main/scala/gitbucket/core/ssh/SshServerListener.scala +++ b/src/main/scala/gitbucket/core/ssh/SshServerListener.scala @@ -5,7 +5,8 @@ import javax.servlet.{ServletContextEvent, ServletContextListener} import gitbucket.core.service.SystemSettingsService -import gitbucket.core.util.Directory +import gitbucket.core.service.SystemSettingsService.SshAddress +import gitbucket.core.util.{Directory} import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider import org.slf4j.LoggerFactory @@ -14,20 +15,20 @@ private val server = org.apache.sshd.server.SshServer.setUpDefaultServer() private val active = new AtomicBoolean(false) - private def configure(port: Int, baseUrl: String) = { - server.setPort(port) + private def configure(sshAddress: SshAddress, baseUrl: String) = { + server.setPort(sshAddress.port) val provider = new SimpleGeneratorHostKeyProvider(new File(s"${Directory.GitBucketHome}/gitbucket.ser")) provider.setAlgorithm("RSA") provider.setOverwriteAllowed(false) server.setKeyPairProvider(provider) server.setPublickeyAuthenticator(new PublicKeyAuthenticator) server.setCommandFactory(new GitCommandFactory(baseUrl)) - server.setShellFactory(new NoShell) + server.setShellFactory(new NoShell(sshAddress)) } - def start(port: Int, baseUrl: String) = { + def start(sshAddress: SshAddress, baseUrl: String) = { if(active.compareAndSet(false, true)){ - configure(port, baseUrl) + configure(sshAddress, baseUrl) server.start() logger.info(s"Start SSH Server Listen on ${server.getPort}") } @@ -55,20 +56,18 @@ override def contextInitialized(sce: ServletContextEvent): Unit = { val settings = loadSystemSettings() - if(settings.ssh){ - settings.baseUrl match { - case None => - logger.error("Could not start SshServer because the baseUrl is not configured.") - case Some(baseUrl) => - SshServer.start(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), baseUrl) - } + if (settings.sshAddress.isDefined && settings.baseUrl.isEmpty) { + logger.error("Could not start SshServer because the baseUrl is not configured.") } + for { + sshAddress <- settings.sshAddress + baseUrl <- settings.baseUrl + } + SshServer.start(sshAddress, baseUrl) } override def contextDestroyed(sce: ServletContextEvent): Unit = { - if(loadSystemSettings().ssh){ - SshServer.stop() - } + SshServer.stop() } } diff --git a/src/main/scala/gitbucket/core/util/Authenticator.scala b/src/main/scala/gitbucket/core/util/Authenticator.scala index 49de665..0035494 100644 --- a/src/main/scala/gitbucket/core/util/Authenticator.scala +++ b/src/main/scala/gitbucket/core/util/Authenticator.scala @@ -36,7 +36,7 @@ private def authenticate(action: (RepositoryInfo) => Any) = { { defining(request.paths){ paths => - getRepository(paths(0), paths(1), baseUrl).map { repository => + getRepository(paths(0), paths(1)).map { repository => context.loginAccount match { case Some(x) if(x.isAdmin) => action(repository) case Some(x) if(repository.owner == x.userName) => action(repository) @@ -95,7 +95,7 @@ private def authenticate(action: (RepositoryInfo) => Any) = { { defining(request.paths){ paths => - getRepository(paths(0), paths(1), baseUrl).map { repository => + getRepository(paths(0), paths(1)).map { repository => context.loginAccount match { case Some(x) if(x.isAdmin) => action(repository) case Some(x) if(paths(0) == x.userName) => action(repository) @@ -118,7 +118,7 @@ private def authenticate(action: (RepositoryInfo) => Any) = { { defining(request.paths){ paths => - getRepository(paths(0), paths(1), baseUrl).map { repository => + getRepository(paths(0), paths(1)).map { repository => if(!repository.repository.isPrivate){ action(repository) } else { @@ -145,7 +145,7 @@ private def authenticate(action: (RepositoryInfo) => Any) = { { defining(request.paths){ paths => - getRepository(paths(0), paths(1), baseUrl).map { repository => + getRepository(paths(0), paths(1)).map { repository => context.loginAccount match { case Some(x) if(x.isAdmin) => action(repository) case Some(x) if(!repository.repository.isPrivate) => action(repository) diff --git a/src/main/scala/gitbucket/core/util/Implicits.scala b/src/main/scala/gitbucket/core/util/Implicits.scala index 131bd98..a7209de 100644 --- a/src/main/scala/gitbucket/core/util/Implicits.scala +++ b/src/main/scala/gitbucket/core/util/Implicits.scala @@ -75,6 +75,11 @@ def gitRepositoryPath: String = request.getRequestURI.replaceFirst("^/git/", "/") + def baseUrl:String = { + val url = request.getRequestURL.toString + val len = url.length - (request.getRequestURI.length - request.getContextPath.length) + url.substring(0, len).stripSuffix("/") + } } implicit class RichSession(session: HttpSession){ diff --git a/src/main/scala/gitbucket/core/util/JGitUtil.scala b/src/main/scala/gitbucket/core/util/JGitUtil.scala index cf23257..2a1182f 100644 --- a/src/main/scala/gitbucket/core/util/JGitUtil.scala +++ b/src/main/scala/gitbucket/core/util/JGitUtil.scala @@ -32,14 +32,13 @@ * * @param owner the user name of the repository owner * @param name the repository name - * @param url the repository URL * @param commitCount the commit count. If the repository has over 1000 commits then this property is 1001. * @param branchList the list of branch names * @param tags the list of tags */ - case class RepositoryInfo(owner: String, name: String, url: String, commitCount: Int, branchList: List[String], tags: List[TagInfo]){ - def this(owner: String, name: String, baseUrl: String) = { - this(owner, name, s"${baseUrl}/git/${owner}/${name}.git", 0, Nil, Nil) + case class RepositoryInfo(owner: String, name: String, commitCount: Int, branchList: List[String], tags: List[TagInfo]){ + def this(owner: String, name: String) = { + this(owner, name, 0, Nil, Nil) } } @@ -174,14 +173,14 @@ /** * Returns the repository information. It contains branch names and tag names. */ - def getRepositoryInfo(owner: String, repository: String, baseUrl: String): RepositoryInfo = { + def getRepositoryInfo(owner: String, repository: String): RepositoryInfo = { using(Git.open(getRepositoryDir(owner, repository))){ git => try { // get commit count val commitCount = git.log.all.call.iterator.asScala.map(_ => 1).take(10001).sum RepositoryInfo( - owner, repository, s"${baseUrl}/git/${owner}/${repository}.git", + owner, repository, // commit count commitCount, // branches @@ -197,7 +196,7 @@ } catch { // not initialized case e: NoHeadException => RepositoryInfo( - owner, repository, s"${baseUrl}/git/${owner}/${repository}.git", 0, Nil, Nil) + owner, repository, 0, Nil, Nil) } } diff --git a/src/main/scala/gitbucket/core/view/Markdown.scala b/src/main/scala/gitbucket/core/view/Markdown.scala index 2d57d1c..449a47b 100644 --- a/src/main/scala/gitbucket/core/view/Markdown.scala +++ b/src/main/scala/gitbucket/core/view/Markdown.scala @@ -60,6 +60,8 @@ pages: List[String]) (implicit val context: Context) extends Renderer(options) with LinkConverter with RequestCache { + private val repositoryUrls = context.urls(repository) + override def heading(text: String, level: Int, raw: String): String = { val id = generateAnchorName(text) val out = new StringBuilder() @@ -135,7 +137,7 @@ (link, link) } - val url = repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/wiki/" + StringUtil.urlEncode(page) + val url = repositoryUrls.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/wiki/" + StringUtil.urlEncode(page) if(pages.contains(page)){ "" + escape(label) + "" } else { @@ -157,14 +159,14 @@ } else if(context.currentPath.contains("/tree/")){ val paths = context.currentPath.split("/") val branch = if(paths.length > 3) paths.drop(4).mkString("/") else repository.repository.defaultBranch - repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + branch + "/" + url + (if(isImage) "?raw=true" else "") + repositoryUrls.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + branch + "/" + url + (if(isImage) "?raw=true" else "") } else { val paths = context.currentPath.split("/") val branch = if(paths.length > 3) paths.last else repository.repository.defaultBranch - repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + branch + "/" + url + (if(isImage) "?raw=true" else "") + repositoryUrls.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + branch + "/" + url + (if(isImage) "?raw=true" else "") } } else { - repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/wiki/_blob/" + url + repositoryUrls.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/wiki/_blob/" + url } } diff --git a/src/main/twirl/gitbucket/core/admin/system.scala.html b/src/main/twirl/gitbucket/core/admin/system.scala.html index 33d94d0..3f07d07 100644 --- a/src/main/twirl/gitbucket/core/admin/system.scala.html +++ b/src/main/twirl/gitbucket/core/admin/system.scala.html @@ -111,15 +111,24 @@ Enable SSH access to git repository -
- -
- - +
+
+ +
+ + +
+
+
+ +
+ + +

- Base URL is required if SSH access is enabled. + Base URL is required if SSH access is enabled.

diff --git a/src/main/twirl/gitbucket/core/main.scala.html b/src/main/twirl/gitbucket/core/main.scala.html index b1d597a..69fcfdc 100644 --- a/src/main/twirl/gitbucket/core/main.scala.html +++ b/src/main/twirl/gitbucket/core/main.scala.html @@ -37,7 +37,7 @@ @repository.map { repository => @if(!repository.repository.isPrivate){ - + } } diff --git a/src/main/twirl/gitbucket/core/menu.scala.html b/src/main/twirl/gitbucket/core/menu.scala.html index b77e6ba..560d16e 100644 --- a/src/main/twirl/gitbucket/core/menu.scala.html +++ b/src/main/twirl/gitbucket/core/menu.scala.html @@ -80,8 +80,8 @@
HTTP clone URL
- @helper.html.copy("repository-url-copy", repository.httpUrl){ - + @helper.html.copy("repository-url-copy", context.urls(repository).httpUrl){ + } @if(settings.ssh && loginAccount.isDefined){
@@ -91,7 +91,7 @@ @id.map { id => @if(context.platform != "linux" && context.platform != null){
-   Clone in Desktop +   Clone in Desktop
}
@@ -184,15 +184,15 @@ @if(settings.ssh && loginAccount.isDefined){ $('#repository-url-http').click(function(){ $('#repository-url-proto').text('HTTP'); - $('#repository-url').val('@repository.httpUrl'); - $('#repository-clone-url').attr('href', '@repository.httpOpenRepoUrl(context.platform)') + $('#repository-url').val('@context.urls(repository).httpUrl'); + $('#repository-clone-url').attr('href', '@context.urls(repository).httpOpenRepoUrl(context.platform)') $('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val()); }); $('#repository-url-ssh').click(function(){ $('#repository-url-proto').text('SSH'); - $('#repository-url').val('@repository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)'); - $('#repository-clone-url').attr('href', '@repository.sshOpenRepoUrl(context.platform, settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)'); + $('#repository-url').val('@context.urls(repository).sshUrl(loginAccount.get.userName)'); + $('#repository-clone-url').attr('href', '@context.urls(repository).sshOpenRepoUrl(context.platform, loginAccount.get.userName)'); $('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val()); }); } diff --git a/src/main/twirl/gitbucket/core/pulls/mergeguide.scala.html b/src/main/twirl/gitbucket/core/pulls/mergeguide.scala.html index ca6ed91..c788a7f 100644 --- a/src/main/twirl/gitbucket/core/pulls/mergeguide.scala.html +++ b/src/main/twirl/gitbucket/core/pulls/mergeguide.scala.html @@ -100,21 +100,21 @@ you can perform a manual merge on the command line.

} - @helper.html.copy("repository-url-copy", forkedRepository.httpUrl, true){ + @helper.html.copy("repository-url-copy", context.urls(forkedRepository).httpUrl, true){
@if(settings.ssh && loginAccount.isDefined){ }
- + }

Step 1: From your project repository, check out a new branch and test the changes.

@defining(s"git checkout -b ${pullreq.requestUserName}-${pullreq.requestBranch} ${pullreq.branch}\n" + - s"git pull ${forkedRepository.httpUrl} ${pullreq.requestBranch}"){ command => + s"git pull ${context.urls(forkedRepository).httpUrl} ${pullreq.requestBranch}"){ command => @helper.html.copy("merge-command-copy-1", command){
@Html(command)
} @@ -174,24 +174,24 @@ @if(settings.ssh && loginAccount.isDefined){ $('#repository-url-http').click(function(){ // Update URL box - $('#repository-url').val('@forkedRepository.httpUrl'); + $('#repository-url').val('@context.urls(forkedRepository).httpUrl'); $('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val()); // Update command guidance $('#merge-command').text($('#merge-command').text().replace( - '@forkedRepository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)', - '@forkedRepository.httpUrl' + '@context.urls(forkedRepository).sshUrl(loginAccount.get.userName)', + '@context.urls(forkedRepository).httpUrl' )); $('#merge-command-copy-1').attr('data-clipboard-text', $('#merge-command').text()); }); $('#repository-url-ssh').click(function(){ // Update URL box - $('#repository-url').val('@forkedRepository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)'); + $('#repository-url').val('@context.urls(forkedRepository).sshUrl(loginAccount.get.userName)'); $('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val()); // Update command guidance $('#merge-command').text($('#merge-command').text().replace( - '@forkedRepository.httpUrl', - '@forkedRepository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)' + '@context.urls(forkedRepository).httpUrl', + '@context.urls(forkedRepository).sshUrl(loginAccount.get.userName)' )); $('#merge-command-copy-1').attr('data-clipboard-text', $('#merge-command').text()); }); diff --git a/src/main/twirl/gitbucket/core/repo/guide.scala.html b/src/main/twirl/gitbucket/core/repo/guide.scala.html index cdb4099..5c064b4 100644 --- a/src/main/twirl/gitbucket/core/repo/guide.scala.html +++ b/src/main/twirl/gitbucket/core/repo/guide.scala.html @@ -10,10 +10,10 @@ } else {

Quick setup — if you've done this kind of thing before

- via HTTP + via HTTP @if(settings.ssh && loginAccount.isDefined){ or - SSH + SSH }

Create a new repository on the command line

@@ -22,12 +22,12 @@ git init git add README.md git commit -m "first commit" - git remote add origin @repository.httpUrl + git remote add origin @context.urls(repository).httpUrl git push -u origin master }

Push an existing repository from the command line

@pre { - git remote add origin @repository.httpUrl + git remote add origin @context.urls(repository).httpUrl git push -u origin master }