~jan0sch/smederee
Showing details for patch e45f55e4bc59fdb6f1fbce591c0c25f5c445d433.
diff -rN -u old-smederee/modules/hub/src/main/scala/de/smederee/hub/VcsRepositoryRoutes.scala new-smederee/modules/hub/src/main/scala/de/smederee/hub/VcsRepositoryRoutes.scala --- old-smederee/modules/hub/src/main/scala/de/smederee/hub/VcsRepositoryRoutes.scala 2025-02-02 16:52:51.162545340 +0000 +++ new-smederee/modules/hub/src/main/scala/de/smederee/hub/VcsRepositoryRoutes.scala 2025-02-02 16:52:51.162545340 +0000 @@ -48,6 +48,134 @@ private val createRepoPath = uri"/repo/create" private val MaximumFileSize = 131072L // TODO Move to configuration directive. + /** Logic for rendering a list of all repositories visible to the given user account. + * + * @param csrf + * An optional CSRF-Token that shall be used. + * @param user + * An optional user account for whom the list of repositories shall be rendered. + * @return + * An HTTP response containing the rendered HTML. + */ + private def doShowAllRepositories(csrf: Option[CsrfToken])(user: Option[Account]): F[Response[F]] = + for { + repos <- vcsMetadataRepo + .listAllRepositories(user)(VcsMetadataRepositoriesOrdering.NameAscending) + .compile + .toList + actionBaseUri <- Sync[F].delay(uri"projects") + resp <- Ok.apply( + views.html.showAllRepositories()(actionBaseUri, csrf, s"Smederee - Projects".some, user)(repos) + ) + } yield resp + + /** Logic for rendering the content of a repository directory or file visible to the given user account. + * + * @param csrf + * An optional CSRF-Token that shall be used. + * @param user + * An optional user account for whom the list of repositories shall be rendered. + * @param repositoryOwnerName + * The name of the user who owns the repository. + * @param repositoryName + * The actual name of the repository. + * @param filePath + * An URI path which describes the path the the requested part of the repository (empty or `/` for the + * root directory of the repo). + * @return + * An HTTP response containing the rendered HTML. + */ + private def doShowRepositoryFiles(csrf: Option[CsrfToken])( + user: Option[Account] + )(repositoryOwnerName: Username, repositoryName: VcsRepositoryName)(filePath: Uri.Path): F[Response[F]] = + for { + owner <- vcsMetadataRepo.findVcsRepositoryOwner(repositoryOwnerName) + loadedRepo <- owner match { + case None => Sync[F].pure(None) + case Some(owner) => vcsMetadataRepo.findVcsRepository(owner, repositoryName) + } + // TODO Replace with whatever we implement as proper permission model. ;-) + repo = user match { + case None => loadedRepo.filter(r => r.isPrivate === false) + case Some(user) => + loadedRepo.filter(r => r.isPrivate === false || r.owner === user.toVcsRepositoryOwner) + } + requestedFilePath <- repo + .traverse(_ => + Sync[F].delay( + os.Path( + Paths.get( + config.repositoriesDirectory.toPath.toString, + repositoryOwnerName.toString, + repositoryName.toString, + filePath.segments.mkString("/") + ) + ) + ) + ) + viewFile <- Sync[F].delay(requestedFilePath.map(os.isFile).getOrElse(false)) + listing <- + requestedFilePath match { + case None => Sync[F].delay(IndexedSeq.empty) + case Some(_) if viewFile => Sync[F].delay(IndexedSeq.empty) + case Some(path) => doListFiles(path) + } + content <- + requestedFilePath match { + case None => Sync[F].delay(IndexedSeq.empty) + case Some(path) => + if (viewFile) + for { + size <- Sync[F].delay(os.size(path)) + stream <- + if (size <= MaximumFileSize) + Sync[F].delay(os.read.lines.stream(path)) + else + Sync[F].delay(os.Generator("File is too big!")) + lines <- Sync[F].delay(stream.toVector) + } yield lines + else + Sync[F].delay(IndexedSeq.empty) + } + fileContent <- Sync[F].delay { + if (content.isEmpty) + None + else + Option(content.mkString("\n")) + } + repositoryBaseUri <- Sync[F].delay( + Uri(path = + Uri.Path.Root |+| Uri.Path( + Vector(Uri.Path.Segment(s"~$repositoryOwnerName"), Uri.Path.Segment(repositoryName.toString)) + ) + ) + ) + actionBaseUri <- Sync[F].delay(Uri(path = repositoryBaseUri.path |+| filePath)) + goBackUri <- Sync[F].delay( + Uri(path = repositoryBaseUri.path |+| Uri.Path(filePath.segments.reverse.drop(1).reverse)) + ) + resp <- + repo match { + case None => NotFound("Repository not found!") + case Some(_) => + if ( + filePath.segments.mkString + .startsWith("_darcs") || filePath.segments.mkString.startsWith("/_darcs") + ) + NotFound("File not found!") + else + Ok( + views.html.showRepository()( + actionBaseUri, + csrf, + Option(goBackUri), + s"Smederee/~$repositoryOwnerName/$repositoryName".some, + user + )(fileContent, listing, repositoryBaseUri, repositoryName) + ) + } + } yield resp + /** List walk the given directory at the first level and return all found files and directories and their * stats sorted by directory first and by name second. If the given path is _not_ a directory then no * traversal is done and an empty list is returned. @@ -57,7 +185,7 @@ * @return * A list of tuples containing a relative path and the related stats. */ - private def listFiles(directory: os.Path): F[IndexedSeq[(os.RelPath, os.StatInfo)]] = + private def doListFiles(directory: os.Path): F[IndexedSeq[(os.RelPath, os.StatInfo)]] = for { isDirectory <- Sync[F].delay(os.isDir(directory)) listing <- @@ -173,14 +301,7 @@ case ar @ GET -> Root / "projects" as user => for { csrf <- Sync[F].delay(ar.req.getCsrfToken) - repos <- vcsMetadataRepo - .listAllRepositories(user.some)(VcsMetadataRepositoriesOrdering.NameAscending) - .compile - .toList - actionBaseUri <- Sync[F].delay(uri"projects") - resp <- Ok.apply( - views.html.showAllRepositories()(actionBaseUri, csrf, s"Smederee - Projects".some, user.some)(repos) - ) + resp <- doShowAllRepositories(csrf)(user.some) } yield resp } @@ -188,14 +309,7 @@ case req @ GET -> Root / "projects" => for { csrf <- Sync[F].delay(req.getCsrfToken) - repos <- vcsMetadataRepo - .listAllRepositories(None)(VcsMetadataRepositoriesOrdering.NameAscending) - .compile - .toList - actionBaseUri <- Sync[F].delay(uri"projects") - resp <- Ok.apply( - views.html.showAllRepositories()(actionBaseUri, csrf, s"Smederee - Projects".some, None)(repos) - ) + resp <- doShowAllRepositories(csrf)(None) } yield resp } @@ -246,109 +360,23 @@ } yield resp } - private val showRepository: AuthedRoutes[Account, F] = AuthedRoutes.of { - case ar @ GET -> Root / UsernamePathParameter(repositoryOwner) / VcsRepositoryNamePathParameter( + private val showRepositoryFiles: AuthedRoutes[Account, F] = AuthedRoutes.of { + case ar @ GET -> UsernamePathParameter(repositoryOwnerName) /: VcsRepositoryNamePathParameter( repositoryName - ) as user => + ) /: filePath as user => for { csrf <- Sync[F].delay(ar.req.getCsrfToken) - directory <- Sync[F].delay( - os.Path( - Paths.get( - config.repositoriesDirectory.toPath.toString, - repositoryOwner.toString, - repositoryName.toString - ) - ) - ) - listing <- listFiles(directory) - repositoryBaseUri <- Sync[F].delay( - Uri(path = - Uri.Path.Root |+| Uri.Path( - Vector(Uri.Path.Segment(s"~$repositoryOwner"), Uri.Path.Segment(repositoryName.toString)) - ) - ) - ) - resp <- Ok( - views.html.showRepository()( - repositoryBaseUri, - csrf, - None, - s"Smederee/~$repositoryOwner/$repositoryName".some, - user - )(None, listing, repositoryBaseUri, repositoryName) - ) + resp <- doShowRepositoryFiles(csrf)(user.some)(repositoryOwnerName, repositoryName)(filePath) } yield resp } - private val showRepositoryFiles: AuthedRoutes[Account, F] = AuthedRoutes.of { - case ar @ GET -> UsernamePathParameter(repositoryOwner) /: VcsRepositoryNamePathParameter( + private val showRepositoryFilesForGuests: HttpRoutes[F] = HttpRoutes.of { + case req @ GET -> UsernamePathParameter(repositoryOwnerName) /: VcsRepositoryNamePathParameter( repositoryName - ) /: filePath as user => + ) /: filePath => for { - csrf <- Sync[F].delay(ar.req.getCsrfToken) - requestedFilePath <- Sync[F].delay( - os.Path( - Paths.get( - config.repositoriesDirectory.toPath.toString, - repositoryOwner.toString, - repositoryName.toString, - filePath.segments.mkString("/") - ) - ) - ) - viewFile <- Sync[F].delay(os.isFile(requestedFilePath)) - listing <- - if (viewFile) - Sync[F].delay(IndexedSeq.empty) - else - listFiles(requestedFilePath) - content <- - if (viewFile) - for { - size <- Sync[F].delay(os.size(requestedFilePath)) - stream <- - if (size <= MaximumFileSize) - Sync[F].delay(os.read.lines.stream(requestedFilePath)) - else - Sync[F].delay(os.Generator("File is too big!")) - lines <- Sync[F].delay(stream.toVector) - } yield lines - else - Sync[F].delay(IndexedSeq.empty) - fileContent <- Sync[F].delay { - if (content.isEmpty) - None - else - Option(content.mkString("\n")) - } - repositoryBaseUri <- Sync[F].delay( - Uri(path = - Uri.Path.Root |+| Uri.Path( - Vector(Uri.Path.Segment(s"~$repositoryOwner"), Uri.Path.Segment(repositoryName.toString)) - ) - ) - ) - actionBaseUri <- Sync[F].delay(Uri(path = repositoryBaseUri.path |+| filePath)) - goBackUri <- Sync[F].delay( - Uri(path = repositoryBaseUri.path |+| Uri.Path(filePath.segments.reverse.drop(1).reverse)) - ) - resp <- - if ( - filePath.segments.mkString - .startsWith("_darcs") || filePath.segments.mkString.startsWith("/_darcs") - ) - NotFound() - else - Ok( - views.html.showRepository()( - actionBaseUri, - csrf, - Option(goBackUri), - s"Smederee/~$repositoryOwner/$repositoryName".some, - user - )(fileContent, listing, repositoryBaseUri, repositoryName) - ) + csrf <- Sync[F].delay(req.getCsrfToken) + resp <- doShowRepositoryFiles(csrf)(None)(repositoryOwnerName, repositoryName)(filePath) } yield resp } @@ -400,8 +428,8 @@ } val protectedRoutes = - showAllRepositories <+> showRepositories <+> parseCreateRepositoryForm <+> showCreateRepositoryForm <+> showRepositoryHistory <+> showRepositoryFiles <+> showRepository + showAllRepositories <+> showRepositories <+> parseCreateRepositoryForm <+> showCreateRepositoryForm <+> showRepositoryHistory <+> showRepositoryFiles - val routes = showAllRepositoriesForGuests + val routes = showAllRepositoriesForGuests <+> showRepositoryFilesForGuests } diff -rN -u old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/showRepository.scala.html new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/showRepository.scala.html --- old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/showRepository.scala.html 2025-02-02 16:52:51.162545340 +0000 +++ new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/showRepository.scala.html 2025-02-02 16:52:51.162545340 +0000 @@ -1,5 +1,5 @@ -@(lang: LanguageCode = LanguageCode("en"), pathPrefix: Option[Uri] = None)(actionBaseUri: Uri, csrf: Option[CsrfToken] = None, goBackUri: Option[Uri] = None, title: Option[String] = None, user: Account)(fileContent: Option[String], listing: IndexedSeq[(os.RelPath, os.StatInfo)], repositoryBaseUri: Uri, repositoryName: VcsRepositoryName) -@main(lang, pathPrefix)()(csrf, title, user.some) { +@(lang: LanguageCode = LanguageCode("en"), pathPrefix: Option[Uri] = None)(actionBaseUri: Uri, csrf: Option[CsrfToken] = None, goBackUri: Option[Uri] = None, title: Option[String] = None, user: Option[Account])(fileContent: Option[String], listing: IndexedSeq[(os.RelPath, os.StatInfo)], repositoryBaseUri: Uri, repositoryName: VcsRepositoryName) +@main(lang, pathPrefix)()(csrf, title, user) { @defining(lang.toLocale) { implicit locale => <div class="content"> <div class="pure-g">