Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

Commit

Permalink
POC: Make authenticated request to display "my repositories" (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlyxPractice authored Jul 25, 2022
1 parent e5db269 commit b9fe20d
Show file tree
Hide file tree
Showing 17 changed files with 422 additions and 200 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,41 @@ docker run --rm -it victorbersy/docker-hub-cli
docker pull ghcr.io/victorbersy/docker-hub-cli:latest
docker run --rm -it ghcr.io/victorbersy/docker-hub-cli
```

## Authentication

The authentication system is rough at the moment. Hopefully, it will be better soon. Meanwhile, here's how to authenticate:

Set `DOCKER_BEARER` with your token and launch the program:

```console
$ DOCKER_BEARER=$DOCKER_BEARER DOCKER_USERNAME=victorbersy go run main.go
```

or with a compiled version:
```console
$ DOCKER_BEARER=$DOCKER_BEARER DOCKER_USERNAME=victorbersy ./docker-hub-cli
```

To get your Bearer token:
```console
$ username=victorbersy \
password=my_docker_pat_token \
curl -s 'https://hub.docker.com/api/v2/users/login' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"username":"'$username'","password":"'$password'"}' | jq -r .token
```

To generate your Personal Access Token (PAT): [hub.docker.com/settings/security](https://hub.docker.com/settings/security)

## Screenshots

### Dark default theme
![image](https://user-images.githubusercontent.com/2109178/180597089-22be7878-8a27-4fe6-8401-be28cc26fa0b.png)
![image](https://user-images.githubusercontent.com/2109178/180597084-c1e26447-91ce-4b82-994f-481886776fad.png)

### Light default theme
![image](https://user-images.githubusercontent.com/2109178/180597083-66beebdb-3b60-401f-ab78-343b866b3986.png)
![image](https://user-images.githubusercontent.com/2109178/180597082-180ff55a-18e1-4056-83a9-26694c1fcc23.png)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package data
package data_search

import (
"fmt"
Expand All @@ -11,11 +11,11 @@ import (
)

type RepositoryPage struct {
PageSize int `json:"page_size"`
PageId int `json:"page"`
NextUrl string `json:"next"`
PreviousUrl string `json:"previous"`
Repositories []RepositoryData `json:"summaries"`
PageSize int `json:"page_size"`
PageId int `json:"page"`
NextUrl string `json:"next"`
PreviousUrl string `json:"previous"`
Repositories []Repository `json:"summaries"`
}

type Category struct {
Expand All @@ -28,7 +28,7 @@ type OperatingSystem struct {
Label string `json:"label"`
}

type RepositoryData struct {
type Repository struct {
Architectures []Architecture `json:"architectures"`
Categories []Category `json:"categories"`
CertificationStatus string `json:"certification_status"`
Expand All @@ -43,7 +43,7 @@ type RepositoryData struct {
Source string `json:"source"`
StarCount int `json:"star_count"`
Type string `json:"type"`
Updated_at time.Time `json:"updated_at"`
UpdatedAt time.Time `json:"updated_at"`
Labels []Label
}

Expand All @@ -64,22 +64,14 @@ type Architecture struct {
Label string `json:"label"`
}

func (data RepositoryData) GetRepoNameWithOwner() string {
return data.Name
}

func (data RepositoryData) GetUrl() string {
func (data Repository) GetUrl() string {
if data.Publisher.Id == "docker" {
return fmt.Sprintf("https://hub.docker.com/_/%s", data.Slug)
}
return fmt.Sprintf("https://hub.docker.com/r/%s", data.Slug)
}

func (data RepositoryData) GetLastUpdate() time.Time {
return data.Updated_at
}

func (data *RepositoryData) setLabels() {
func (data *Repository) setLabels() {
data.Labels = append(data.Labels, Label{
Name: "Docker Official",
Glyph: constants.GlyphLabelDockerOfficial,
Expand All @@ -106,7 +98,7 @@ func (data *RepositoryData) setLabels() {
})
}

func FetchRepositories() ([]RepositoryData, error) {
func FetchRepositories() ([]Repository, error) {
client := req.C().
SetTimeout(5 * time.Second)

Expand Down
69 changes: 69 additions & 0 deletions internal/data/user/repositories.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package data_user

import (
"fmt"
"log"
"os"
"time"

"github.com/imroc/req/v3"
)

type RepositoryPage struct {
Count int `json:"count"`
NextUrl string `json:"next"`
PreviousUrl string `json:"previous"`
Repositories []Repository `json:"results"`
}

type Repository struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
RepositoryType string `json:"repository_type"`
Status int `json:"status"`
IsPrivate bool `json:"is_private"`
StarCount int `json:"star_count"`
PullCount int `json:"pull_count"`
UpdatedAt time.Time `json:"last_updated"`
CreatedAt time.Time `json:"date_registered"`
Affiliation string `json:"affiliation"`
}

func (data Repository) GetUrl() string {
return fmt.Sprintf("https://hub.docker.com/repository/docker/%s/%s", data.Namespace, data.Name)
}

func FetchRepositories() ([]Repository, error) {
client := req.C().
SetTimeout(5 * time.Second)

var repositoryPage RepositoryPage
if os.Getenv("DOCKER_USERNAME") == "" {
return nil, nil
}
repo_url := fmt.Sprintf("https://hub.docker.com/v2/repositories/%s", os.Getenv("DOCKER_USERNAME"))
resp, err := client.R().
SetHeader("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("DOCKER_BEARER"))).
SetResult(&repositoryPage).
EnableDump().
SetQueryParam("page_size", "100").
SetQueryParam("page", "1").
SetQueryParam("ordering", "last_updated").
Get(repo_url)
if err != nil {
log.Println("error:", err)
log.Println("raw content:")
log.Println(resp.Dump())
return nil, err
}

if resp.IsSuccess() {
return repositoryPage.Repositories, err
}

log.Println("unknown status", resp.Status)
log.Println("raw content:")
log.Println(resp.Dump()) // Record raw content when server returned unknown status code.

return repositoryPage.Repositories, err
}
4 changes: 0 additions & 4 deletions internal/data/utils.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package data

import "time"

type RowData interface {
GetRepoNameWithOwner() string
GetUrl() string
GetLastUpdate() time.Time
}
69 changes: 0 additions & 69 deletions internal/ui/components/repository/repository.go
Original file line number Diff line number Diff line change
@@ -1,70 +1 @@
package repository

import (
"fmt"

"github.com/charmbracelet/lipgloss"
"github.com/victorbersy/docker-hub-cli/internal/data"
"github.com/victorbersy/docker-hub-cli/internal/ui/components/table"
"github.com/victorbersy/docker-hub-cli/internal/utils"
)

type Repository struct {
Data data.RepositoryData
}

func (repo Repository) ToTableRow() table.Row {
return table.Row{
repo.renderName(),
repo.renderLabels(),
repo.renderPublisher(),
repo.renderstatsDownloads(),
repo.renderstatsStars(),
repo.renderLastUpdate(),
repo.renderDescription(),
}
}

func (repo Repository) renderName() string {
return repo.Data.Name
}

func (repo Repository) renderLabels() string {
labels := []string{}
for _, label := range repo.Data.Labels {
if label.Enabled {
labels = append(labels, lipgloss.NewStyle().Foreground(label.Color).Width(3).Render(label.Glyph))
} else {
labels = append(labels, lipgloss.NewStyle().Width(3).Render(""))
}
}
return lipgloss.JoinHorizontal(
lipgloss.Top,
labels...,
)
}

func (repo Repository) renderPublisher() string {
return lipgloss.NewStyle().
Render(repo.Data.Publisher.Name)
}

func (repo Repository) renderstatsDownloads() string {
return lipgloss.NewStyle().
Render(repo.Data.PullCount)
}

func (repo Repository) renderstatsStars() string {
return lipgloss.NewStyle().
Render(fmt.Sprint(repo.Data.StarCount))
}

func (repo Repository) renderDescription() string {
return lipgloss.NewStyle().
Render(repo.Data.Description)
}

func (repo Repository) renderLastUpdate() string {
return lipgloss.NewStyle().
Render(utils.TimeElapsed(repo.Data.Updated_at))
}
70 changes: 70 additions & 0 deletions internal/ui/components/repository/search/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package repository_search

import (
"fmt"

"github.com/charmbracelet/lipgloss"
data_search "github.com/victorbersy/docker-hub-cli/internal/data/search"
"github.com/victorbersy/docker-hub-cli/internal/ui/components/table"
"github.com/victorbersy/docker-hub-cli/internal/utils"
)

type Repository struct {
Data data_search.Repository
}

func (repo Repository) ToTableRow() table.Row {
return table.Row{
repo.renderName(),
repo.renderLabels(),
repo.renderPublisher(),
repo.renderstatsDownloads(),
repo.renderstatsStars(),
repo.renderLastUpdate(),
repo.renderDescription(),
}
}

func (repo Repository) renderName() string {
return repo.Data.Name
}

func (repo Repository) renderLabels() string {
labels := []string{}
for _, label := range repo.Data.Labels {
if label.Enabled {
labels = append(labels, lipgloss.NewStyle().Foreground(label.Color).Width(3).Render(label.Glyph))
} else {
labels = append(labels, lipgloss.NewStyle().Width(3).Render(""))
}
}
return lipgloss.JoinHorizontal(
lipgloss.Top,
labels...,
)
}

func (repo Repository) renderPublisher() string {
return lipgloss.NewStyle().
Render(repo.Data.Publisher.Name)
}

func (repo Repository) renderstatsDownloads() string {
return lipgloss.NewStyle().
Render(repo.Data.PullCount)
}

func (repo Repository) renderstatsStars() string {
return lipgloss.NewStyle().
Render(fmt.Sprint(repo.Data.StarCount))
}

func (repo Repository) renderDescription() string {
return lipgloss.NewStyle().
Render(repo.Data.Description)
}

func (repo Repository) renderLastUpdate() string {
return lipgloss.NewStyle().
Render(utils.TimeElapsed(repo.Data.UpdatedAt))
}
58 changes: 58 additions & 0 deletions internal/ui/components/repository/user/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package repository_user

import (
"fmt"

"github.com/charmbracelet/lipgloss"
data_user "github.com/victorbersy/docker-hub-cli/internal/data/user"
"github.com/victorbersy/docker-hub-cli/internal/ui/components/table"
"github.com/victorbersy/docker-hub-cli/internal/ui/constants"
"github.com/victorbersy/docker-hub-cli/internal/utils"
)

type Repository struct {
Data data_user.Repository
}

func (repo Repository) ToTableRow() table.Row {
return table.Row{
repo.renderName(),
repo.renderIsPrivate(),
repo.renderstatsDownloads(),
repo.renderstatsStars(),
repo.renderLastUpdate(),
repo.renderCreatedAt(),
}
}

func (repo Repository) renderName() string {
return repo.Data.Name
}

func (repo Repository) renderIsPrivate() string {
if repo.Data.IsPrivate {
return lipgloss.NewStyle().Foreground(lipgloss.Color("#EF476F")).Render(constants.GlyphPrivate)
} else {
return lipgloss.NewStyle().Foreground(lipgloss.Color("#06D6A0")).Render(constants.GlyphPublic)
}
}

func (repo Repository) renderstatsDownloads() string {
return lipgloss.NewStyle().
Render(fmt.Sprint(repo.Data.PullCount))
}

func (repo Repository) renderstatsStars() string {
return lipgloss.NewStyle().
Render(fmt.Sprint(repo.Data.StarCount))
}

func (repo Repository) renderLastUpdate() string {
return lipgloss.NewStyle().
Render(utils.TimeElapsed(repo.Data.UpdatedAt))
}

func (repo Repository) renderCreatedAt() string {
return lipgloss.NewStyle().
Render(utils.TimeElapsed(repo.Data.CreatedAt))
}
Loading

0 comments on commit b9fe20d

Please sign in to comment.