Compare commits

...
Author SHA1 Message Date
Tiago Vasconcelos 139bd96639 chore: bundle 2026-07-14 16:25:28 +01:00
Tiago Vasconcelos f6114fc33a update i18n 2026-07-14 16:12:08 +01:00
Tiago Vasconcelos 399e01c6a8 reduce ui inline conditionals 2026-07-14 16:12:08 +01:00
Tiago Vasconcelos abe1e2fb27 display expiry on ACL tokens 2026-07-14 16:12:08 +01:00
8 changed files with 56 additions and 6 deletions
+1
View File
@@ -41,6 +41,7 @@ class SimpleStatus(BaseModel):
class SimpleItem(BaseModel):
id: str
name: str
expires_at: int | None = None
class DbVersion(BaseModel):
+4 -1
View File
@@ -295,7 +295,10 @@ async def api_create_user_api_token(
account.username, api_token_id, data.expiration_time_minutes
)
acl.token_id_list.append(SimpleItem(id=api_token_id, name=data.token_name))
expires_at = int(time()) + data.expiration_time_minutes * 60
acl.token_id_list.append(
SimpleItem(id=api_token_id, name=data.token_name, expires_at=expires_at)
)
await update_user_access_control_list(acls)
return ApiTokenResponse(id=api_token_id, api_token=api_token)
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+2
View File
@@ -482,6 +482,8 @@ window.localisation.en = {
access_control_list_admin_warning:
'This is an admin account. The generated tokens will have admin privileges.',
new_api_acl: 'New Access Control List',
acl_token_active: 'Active',
acl_token_expired: 'Expired',
api_token_id: 'Token Id',
toggle_gradient: 'Toggle Gradient',
gradient_background: 'Gradient Background',
+29
View File
@@ -217,6 +217,35 @@ window.PageAccount = {
computed: {
isUserTouched() {
return !_.isEqual(this.g.user, this.untouchedUser)
},
selectedApiToken() {
return this.selectedApiAcl.token_id_list.find(
token => token.id === this.apiAcl.selectedTokenId
)
},
expiryAt() {
if (this.selectedApiToken.expires_at) {
return `${this.$t('expiry')}: ${LNbits.utils.formatTimestamp(this.selectedApiToken.expires_at)}`
} else {
return ''
}
},
tokenStatus() {
if (this.selectedApiToken.expires_at) {
const now = new Date()
const expiresAt = new Date(this.selectedApiToken.expires_at * 1000)
let status = ''
let badgeColor = 'positive'
if (expiresAt < now) {
status = this.$t('acl_token_expired')
badgeColor = 'negative'
} else {
status = this.$t('acl_token_active')
}
return {status, badgeColor}
} else {
return ''
}
}
},
methods: {
+11
View File
@@ -889,6 +889,17 @@
></q-btn>
</div>
</div>
<div
v-if="selectedApiToken && selectedApiToken.expires_at"
class="row items-center q-mb-md q-gutter-sm"
>
<span v-text="expiryAt"></span>
<span v-text="$t('status') + ':'"></span>
<q-badge
:color="tokenStatus.badgeColor"
:label="tokenStatus.status"
></q-badge>
</div>
<div v-if="apiAcl.apiToken" class="row q-mb-md">
<div class="col-12">
<q-badge>
+7 -3
View File
@@ -1745,10 +1745,14 @@ async def test_api_create_user_api_token_success(
), "Expiration time should be 60 minutes from now."
token_id = payload["api_token_id"]
assert any(
token_id in [token.id for token in acl.token_id_list]
stored_token = next(
token
for acl in acls.access_control_list
), "API token should be part of at least one ACL."
for token in acl.token_id_list
if token.id == token_id
)
assert stored_token.expires_at is not None
assert abs(stored_token.expires_at - expiration_time) <= 1
@pytest.mark.anyio