feat: return time of last usage for public keys and access tokens in the api (#34323)

In the Gitea GUI, the user can see the time that _AccessTokens_ and
_PublicKeys_ were last used. This information is not returned by the
_/users/{username}/tokens_ and _/user/keys_ endpoints in the API. This
PR adds the missing data.

The time of last usage for for _tokens_ & _keys_ seem to be stored in
the _Updated_ field of the structs internally. For consistency, I have
used the name _updated_at_ for the new field returned by the _API_.
However, for the _API_ user, I don't think that name reflects the data
returned, as I believe it is the time of last usage. I propose that we
use the name _last_used_at_ instead. Let's hear reviewers opinion on
that.

* PublicKey
  1. _last_used_at_: string($date-time)
* AccessToken
  1. _created_at_: string($date-time) (for parity with public keys)
  2. _last_used_at_: string($date-time)

Fix #34313
This commit is contained in:
Tobias Balle-Petersen 2025-05-01 20:42:17 +02:00 committed by GitHub
parent e67f74efc8
commit 3e49fba578
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 26 additions and 5 deletions

View File

@ -11,11 +11,13 @@ import (
// AccessToken represents an API access token. // AccessToken represents an API access token.
// swagger:response AccessToken // swagger:response AccessToken
type AccessToken struct { type AccessToken struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Token string `json:"sha1"` Token string `json:"sha1"`
TokenLastEight string `json:"token_last_eight"` TokenLastEight string `json:"token_last_eight"`
Scopes []string `json:"scopes"` Scopes []string `json:"scopes"`
Created time.Time `json:"created_at"`
Updated time.Time `json:"last_used_at"`
} }
// AccessTokenList represents a list of API access token. // AccessTokenList represents a list of API access token.

View File

@ -16,6 +16,7 @@ type PublicKey struct {
Fingerprint string `json:"fingerprint,omitempty"` Fingerprint string `json:"fingerprint,omitempty"`
// swagger:strfmt date-time // swagger:strfmt date-time
Created time.Time `json:"created_at,omitempty"` Created time.Time `json:"created_at,omitempty"`
Updated time.Time `json:"last_used_at,omitempty"`
Owner *User `json:"user,omitempty"` Owner *User `json:"user,omitempty"`
ReadOnly bool `json:"read_only,omitempty"` ReadOnly bool `json:"read_only,omitempty"`
KeyType string `json:"key_type,omitempty"` KeyType string `json:"key_type,omitempty"`

View File

@ -62,6 +62,8 @@ func ListAccessTokens(ctx *context.APIContext) {
Name: tokens[i].Name, Name: tokens[i].Name,
TokenLastEight: tokens[i].TokenLastEight, TokenLastEight: tokens[i].TokenLastEight,
Scopes: tokens[i].Scope.StringSlice(), Scopes: tokens[i].Scope.StringSlice(),
Created: tokens[i].CreatedUnix.AsTime(),
Updated: tokens[i].UpdatedUnix.AsTime(),
} }
} }

View File

@ -316,6 +316,7 @@ func ToPublicKey(apiLink string, key *asymkey_model.PublicKey) *api.PublicKey {
Title: key.Name, Title: key.Name,
Fingerprint: key.Fingerprint, Fingerprint: key.Fingerprint,
Created: key.CreatedUnix.AsTime(), Created: key.CreatedUnix.AsTime(),
Updated: key.UpdatedUnix.AsTime(),
} }
} }

View File

@ -19978,11 +19978,21 @@
"type": "object", "type": "object",
"title": "AccessToken represents an API access token.", "title": "AccessToken represents an API access token.",
"properties": { "properties": {
"created_at": {
"type": "string",
"format": "date-time",
"x-go-name": "Created"
},
"id": { "id": {
"type": "integer", "type": "integer",
"format": "int64", "format": "int64",
"x-go-name": "ID" "x-go-name": "ID"
}, },
"last_used_at": {
"type": "string",
"format": "date-time",
"x-go-name": "Updated"
},
"name": { "name": {
"type": "string", "type": "string",
"x-go-name": "Name" "x-go-name": "Name"
@ -25477,6 +25487,11 @@
"type": "string", "type": "string",
"x-go-name": "KeyType" "x-go-name": "KeyType"
}, },
"last_used_at": {
"type": "string",
"format": "date-time",
"x-go-name": "Updated"
},
"read_only": { "read_only": {
"type": "boolean", "type": "boolean",
"x-go-name": "ReadOnly" "x-go-name": "ReadOnly"