chore(front): convert auth-tokens to svelte
This commit is contained in:
parent
3abd692ce3
commit
c54a201e98
@ -1,22 +1,31 @@
|
|||||||
{% extends 'layouts' %}
|
<script lang="ts">
|
||||||
|
import {locals} from "../ts/stores.js";
|
||||||
|
import {route} from "../../common/Routing.js";
|
||||||
|
import {Time} from "../../common/Time.js";
|
||||||
|
import BaseTemplate from "./templates/BaseTemplate.svelte";
|
||||||
|
import Form from "./utils/Form.svelte";
|
||||||
|
import Icon from "./utils/Icon.svelte";
|
||||||
|
import CopyableText from "./components/CopyableText.svelte";
|
||||||
|
import Pagination from "./components/Pagination.svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
{% set title = app.name + ' - Auth tokens' %}
|
<BaseTemplate title="{$locals.app.name} - Auth tokens" description="Upload files directly from your desktop." noH1>
|
||||||
|
<h1>Auth tokens</h1>
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div class="container">
|
|
||||||
<section class="panel">
|
<section class="panel">
|
||||||
<h2><i data-feather="key"></i> Auth tokens</h2>
|
<h2>
|
||||||
<form action="{{ route('generate-token') }}" method="POST">
|
<Icon name="key"/>
|
||||||
{{ macros.csrf(getCsrfToken) }}
|
Auth tokens
|
||||||
|
</h2>
|
||||||
|
<Form action="{route('generate-token')}" submitText="Generate a new token" submitIcon="plus"/>
|
||||||
|
|
||||||
<button type="submit"><i data-feather="plus"></i> Generate a new token</button>
|
<Pagination pagination={$locals.pagination} routeName="auth-tokens" contextSize={3}/>
|
||||||
</form>
|
|
||||||
|
|
||||||
<table class="data-table">
|
<table class="data-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th class="table-col-grow">Secret</th>
|
<th class="col-grow">Secret</th>
|
||||||
<th>Created at</th>
|
<th>Created at</th>
|
||||||
<th>Last used at</th>
|
<th>Last used at</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
@ -24,40 +33,52 @@
|
|||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for token in auth_tokens %}
|
{#each $locals.auth_tokens as token}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ token.id }}</td>
|
<td>{token.id}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="copyable-text">
|
<CopyableText content="{token.secret}"/>
|
||||||
<div class="content">{{ token.secret }}</div>
|
</td>
|
||||||
<button class="copy-button"><i data-feather="copy"></i></button>
|
<td>
|
||||||
</div>
|
<time datetime="{token.created_at}" title="{token.created_at}">
|
||||||
|
{Time.humanizeTimeSince(new Date(token.created_at), true)} ago
|
||||||
|
</time>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{#if token.used_at}
|
||||||
|
<time datetime="{token.used_at}" title="{token.used_at}">
|
||||||
|
{Time.humanizeTimeSince(new Date(token.used_at), true)} ago
|
||||||
|
</time>
|
||||||
|
{:else}
|
||||||
|
Never
|
||||||
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ token.created_at.toISOString() }}</td>
|
|
||||||
<td>{{ token.used_at.toISOString() }}</td>
|
|
||||||
<td class="actions">
|
<td class="actions">
|
||||||
<form action="{{ route('revoke-token', token.id) }}" method="POST">
|
<Form action="{route('revoke-token', token.id)}" submitText="Revoke" submitIcon="trash"
|
||||||
<button class="button danger"><i data-feather="trash"></i> <span class="tip">Revoke</span></button>
|
submitClass="danger"/>
|
||||||
{{ macros.csrf(getCsrfToken) }}
|
|
||||||
</form>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<Pagination pagination={$locals.pagination} routeName="auth-tokens" contextSize={3}/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
<section class="panel">
|
<section class="panel">
|
||||||
<h2><i data-feather="tool"></i> Setup a desktop utility</h2>
|
<h2>
|
||||||
|
<Icon name="wrench"/>
|
||||||
|
Setup a desktop utility
|
||||||
|
</h2>
|
||||||
<p>There may be a desktop client at some point. For now, if you're an advanced user, you can setup
|
<p>There may be a desktop client at some point. For now, if you're an advanced user, you can setup
|
||||||
scripts/macros.</p>
|
scripts/macros.</p>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<section>
|
<section>
|
||||||
<h3>First alternative: sh script (native on linux)</h3>
|
<h3>First alternative: sh script (native on linux)</h3>
|
||||||
<p>If you have the sh shell on your machine (i.e. you are on linux, git bash on windows...) and curl, you can
|
<p>If you have the sh shell on your machine (i.e. you are on linux, git bash on windows...) and curl, you
|
||||||
download and use these scripts:</p>
|
can download and use these scripts:</p>
|
||||||
<table class="data-table">
|
<table class="data-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -68,11 +89,11 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>upload_file.sh</td>
|
<td>upload_file.sh</td>
|
||||||
<td><a href="{{ route('file-linux-script') }}">Download</a></td>
|
<td><a href="{route('file-linux-script')}">Download</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>shrink_url.sh</td>
|
<td>shrink_url.sh</td>
|
||||||
<td><a href="{{ route('url-linux-script') }}">Download</a></td>
|
<td><a href="{route('url-linux-script')}">Download</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -134,7 +155,7 @@
|
|||||||
<td>url_domain</td>
|
<td>url_domain</td>
|
||||||
<td>Choose domain name</td>
|
<td>Choose domain name</td>
|
||||||
<td>No</td>
|
<td>No</td>
|
||||||
<td>{{ allowed_domains.join('|') }}</td>
|
<td>{$locals.allowed_domains.join('|')}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
@ -156,12 +177,11 @@
|
|||||||
<td>url_domain</td>
|
<td>url_domain</td>
|
||||||
<td>Choose domain name</td>
|
<td>Choose domain name</td>
|
||||||
<td>No</td>
|
<td>No</td>
|
||||||
<td>{{ allowed_domains.join('|') }}</td>
|
<td>{$locals.allowed_domains.join('|')}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p>For examples with curl, please download and review the scripts above.</p>
|
<p>For examples with curl, please download and review the scripts above.</p>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</BaseTemplate>
|
||||||
{% endblock %}
|
|
@ -9,7 +9,7 @@ import AuthToken from "../models/AuthToken.js";
|
|||||||
|
|
||||||
export default class AuthTokenController extends Controller {
|
export default class AuthTokenController extends Controller {
|
||||||
public routes(): void {
|
public routes(): void {
|
||||||
this.get('/auth-tokens', this.getAuthTokens, 'auth-tokens', RequireAuthMiddleware);
|
this.get('/auth-tokens/:page?', this.getAuthTokens, 'auth-tokens', RequireAuthMiddleware);
|
||||||
this.post('/gen-auth-token', this.postGenAuthToken, 'generate-token', RequireAuthMiddleware);
|
this.post('/gen-auth-token', this.postGenAuthToken, 'generate-token', RequireAuthMiddleware);
|
||||||
this.post('/revoke-auth-token/:id', this.postRevokeAuthToken, 'revoke-token', RequireAuthMiddleware);
|
this.post('/revoke-auth-token/:id', this.postRevokeAuthToken, 'revoke-token', RequireAuthMiddleware);
|
||||||
}
|
}
|
||||||
@ -17,10 +17,16 @@ export default class AuthTokenController extends Controller {
|
|||||||
public async getAuthTokens(req: Request, res: Response): Promise<void> {
|
public async getAuthTokens(req: Request, res: Response): Promise<void> {
|
||||||
const allowedDomains = config.get<string[]>('allowed_url_domains');
|
const allowedDomains = config.get<string[]>('allowed_url_domains');
|
||||||
const user = req.as(RequireAuthMiddleware).getUser();
|
const user = req.as(RequireAuthMiddleware).getUser();
|
||||||
|
const authTokens = await AuthToken.paginateForUser(req, 2, user.getOrFail('id'));
|
||||||
res.render('auth-tokens', {
|
res.render('auth-tokens', {
|
||||||
allowed_domains: allowedDomains,
|
allowed_domains: allowedDomains,
|
||||||
default_domain: allowedDomains[config.get<number>('default_url_domain_for_files')],
|
default_domain: allowedDomains[config.get<number>('default_url_domain_for_files')],
|
||||||
auth_tokens: await AuthToken.select().where('user_id', user.id).get(),
|
auth_tokens: authTokens.map(token => ({
|
||||||
|
...token,
|
||||||
|
created_at: token.created_at?.toISOString(),
|
||||||
|
used_at: token.used_at?.toISOString(),
|
||||||
|
})),
|
||||||
|
pagination: authTokens.pagination?.serialize(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,26 @@
|
|||||||
|
import {Request} from "express";
|
||||||
import {nanoid} from "nanoid";
|
import {nanoid} from "nanoid";
|
||||||
import AuthProof from "swaf/auth/AuthProof";
|
import AuthProof from "swaf/auth/AuthProof";
|
||||||
import User from "swaf/auth/models/User";
|
import User from "swaf/auth/models/User";
|
||||||
import Model from "swaf/db/Model";
|
import Model from "swaf/db/Model";
|
||||||
|
import {ModelQueryResult} from "swaf/db/ModelQuery";
|
||||||
|
|
||||||
export default class AuthToken extends Model implements AuthProof<User> {
|
export default class AuthToken extends Model implements AuthProof<User> {
|
||||||
|
public static async paginateForUser(
|
||||||
|
req: Request,
|
||||||
|
perPage: number,
|
||||||
|
user_id: number,
|
||||||
|
): Promise<ModelQueryResult<AuthToken>> {
|
||||||
|
req.params.sortBy = 'created_at';
|
||||||
|
req.params.sortDirection = 'DESC';
|
||||||
|
return await this.paginate(req, perPage, this.select().where('user_id', user_id));
|
||||||
|
}
|
||||||
|
|
||||||
public id?: number = undefined;
|
public id?: number = undefined;
|
||||||
protected readonly user_id?: number = undefined;
|
protected readonly user_id?: number = undefined;
|
||||||
private secret?: string = undefined;
|
private secret?: string = undefined;
|
||||||
protected created_at?: Date = undefined;
|
public created_at?: Date = undefined;
|
||||||
protected used_at?: Date = undefined;
|
public used_at?: Date = undefined;
|
||||||
protected readonly ttl?: number = undefined;
|
protected readonly ttl?: number = undefined;
|
||||||
|
|
||||||
protected init(): void {
|
protected init(): void {
|
||||||
@ -23,6 +35,9 @@ export default class AuthToken extends Model implements AuthProof<User> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO promote this method to AuthProof interface in swaf and call it on successful authentication
|
||||||
|
*/
|
||||||
public use(): void {
|
public use(): void {
|
||||||
this.used_at = new Date();
|
this.used_at = new Date();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user