Exposing stashes on scenes GraphQL object.
This commit is contained in:
parent
acef14b02c
commit
f5d8c30ff3
|
@ -21,78 +21,80 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="manager">
|
||||||
v-if="newKey"
|
<div
|
||||||
class="newkey"
|
v-if="newKey"
|
||||||
>
|
class="newkey"
|
||||||
<p class="key-info">Successfully generated key with identifier <strong class="newkey-identifier ellipsis">{{ newKey.identifier }}</strong>:</p>
|
|
||||||
|
|
||||||
<input
|
|
||||||
:value="newKey.key"
|
|
||||||
class="input ellipsis"
|
|
||||||
@click="copyKey"
|
|
||||||
>
|
>
|
||||||
|
<p class="key-info">Successfully generated key with identifier <strong class="newkey-identifier ellipsis">{{ newKey.identifier }}</strong>:</p>
|
||||||
|
|
||||||
<p class="key-info">Please store this key securely, you will <strong>not</strong> be able to retrieve it later. If you lose it, you must generate a new key.</p>
|
<input
|
||||||
</div>
|
:value="newKey.key"
|
||||||
|
class="input ellipsis"
|
||||||
|
@click="copyKey"
|
||||||
|
>
|
||||||
|
|
||||||
<ul
|
<p class="key-info">Please store this key securely, you will <strong>not</strong> be able to retrieve it later. If you lose it, you must generate a new key.</p>
|
||||||
v-if="keys.length > 0"
|
</div>
|
||||||
class="keys nolist"
|
|
||||||
>
|
<ul
|
||||||
<li
|
v-if="keys.length > 0"
|
||||||
v-for="key in keys"
|
class="keys nolist"
|
||||||
:key="`key-${key.id}`"
|
|
||||||
class="key"
|
|
||||||
>
|
>
|
||||||
<div class="key-row key-header">
|
<li
|
||||||
<strong class="key-value key-identifier ellipsis">{{ key.identifier }}</strong>
|
v-for="key in keys"
|
||||||
|
:key="`key-${key.id}`"
|
||||||
|
class="key"
|
||||||
|
>
|
||||||
|
<div class="key-row key-header">
|
||||||
|
<strong class="key-value key-identifier ellipsis">{{ key.identifier }}</strong>
|
||||||
|
|
||||||
<span class="key-actions">
|
<span class="key-actions">
|
||||||
<Icon
|
<Icon
|
||||||
icon="bin"
|
icon="bin"
|
||||||
class="key-remove"
|
class="key-remove"
|
||||||
@click="removeKey(key)"
|
@click="removeKey(key)"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="key-row key-details">
|
<div class="key-row key-details">
|
||||||
<span class="key-value key-created">
|
<span class="key-value key-created">
|
||||||
<Icon icon="plus-circle" />
|
<Icon icon="plus-circle" />
|
||||||
|
|
||||||
<time
|
|
||||||
v-tooltip="`Created ${format(key.createdAt, 'yyyy-MM-dd hh:mm:ss')}`"
|
|
||||||
:datetime="key.createdAt.toISOString()"
|
|
||||||
>{{ formatDistanceStrict(key.createdAt, now) }} ago</time>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="key-value key-used">
|
|
||||||
<Icon icon="history" />
|
|
||||||
|
|
||||||
<template v-if="key.lastUsedAt">
|
|
||||||
<time
|
<time
|
||||||
v-tooltip="`Last used ${format(key.lastUsedAt, 'yyyy-MM-dd hh:mm:ss')} from IP ${key.lastUsedIp}`"
|
v-tooltip="`Created ${format(key.createdAt, 'yyyy-MM-dd hh:mm:ss')}`"
|
||||||
:datetime="key.lastUsedAt.toISOString()"
|
:datetime="key.createdAt.toISOString()"
|
||||||
>{{ formatDistanceStrict(key.lastUsedAt, now) }} ago</time>
|
>{{ formatDistanceStrict(key.createdAt, now) }} ago</time>
|
||||||
</template>
|
</span>
|
||||||
|
|
||||||
<template v-else>Never</template>
|
<span class="key-value key-used">
|
||||||
</span>
|
<Icon icon="history" />
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div
|
<template v-if="key.lastUsedAt">
|
||||||
v-if="keys.length > 0"
|
<time
|
||||||
class="info"
|
v-tooltip="`Last used ${format(key.lastUsedAt, 'yyyy-MM-dd hh:mm:ss')} from IP ${key.lastUsedIp}`"
|
||||||
>
|
:datetime="key.lastUsedAt.toISOString()"
|
||||||
<h3 class="info-heading">HTTP headers</h3>
|
>{{ formatDistanceStrict(key.lastUsedAt, now) }} ago</time>
|
||||||
|
</template>
|
||||||
|
|
||||||
<code class="headers">
|
<template v-else>Never</template>
|
||||||
API-User: {{ user.id }}<br>
|
</span>
|
||||||
API-Key: YourSecurelyStoredApiKey12345678
|
</div>
|
||||||
</code>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="keys.length > 0"
|
||||||
|
class="info"
|
||||||
|
>
|
||||||
|
<h3 class="info-heading">HTTP headers</h3>
|
||||||
|
|
||||||
|
<code class="headers">
|
||||||
|
API-User: {{ user.id }}<br>
|
||||||
|
API-Key: YourSecurelyStoredApiKey12345678
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
@ -151,18 +153,6 @@ function copyKey(event) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.page {
|
|
||||||
display: flex;
|
|
||||||
flex-grow: 1;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.manager {
|
|
||||||
width: 1200px;
|
|
||||||
max-width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.keys-header {
|
.keys-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
@ -297,7 +287,17 @@ function copyKey(event) {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media(--compact) {
|
||||||
|
.manager {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media(--small-20) {
|
@media(--small-20) {
|
||||||
|
.manager {
|
||||||
|
padding: 0 .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.keys {
|
.keys {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import Router from 'express-promise-router';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
fetchAlerts,
|
fetchAlerts,
|
||||||
createAlert,
|
createAlert,
|
||||||
|
@ -7,18 +9,124 @@ import {
|
||||||
updateNotification,
|
updateNotification,
|
||||||
} from '../alerts.js';
|
} from '../alerts.js';
|
||||||
|
|
||||||
|
export const alertsSchema = `
|
||||||
|
extend type Query {
|
||||||
|
alerts: [Alert]
|
||||||
|
|
||||||
|
alert(
|
||||||
|
id: Int!
|
||||||
|
): Alert
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Mutation {
|
||||||
|
createAlert(
|
||||||
|
all: Boolean = true
|
||||||
|
allActors: Boolean = true
|
||||||
|
allTags: Boolean = true
|
||||||
|
allMatches: Boolean = true
|
||||||
|
actors: [Int!]
|
||||||
|
tags: [Int!]
|
||||||
|
entities: [Int!]
|
||||||
|
matches: [AlertMatchInput!]
|
||||||
|
notify: Boolean = true
|
||||||
|
email: Boolean = false
|
||||||
|
stashes: [Int!]
|
||||||
|
): Alert
|
||||||
|
}
|
||||||
|
|
||||||
|
type AlertAnd {
|
||||||
|
fields: Boolean
|
||||||
|
actors: Boolean
|
||||||
|
tags: Boolean
|
||||||
|
entities: Boolean
|
||||||
|
matches: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type AlertActor {
|
||||||
|
id: Int
|
||||||
|
name: String
|
||||||
|
slug: String
|
||||||
|
}
|
||||||
|
|
||||||
|
type AlertTag {
|
||||||
|
id: Int
|
||||||
|
name: String
|
||||||
|
slug: String
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EntityType {
|
||||||
|
network
|
||||||
|
channel
|
||||||
|
studio
|
||||||
|
info
|
||||||
|
}
|
||||||
|
|
||||||
|
type AlertEntity {
|
||||||
|
id: Int
|
||||||
|
name: String
|
||||||
|
slug: String
|
||||||
|
type: EntityType
|
||||||
|
}
|
||||||
|
|
||||||
|
type AlertMatch {
|
||||||
|
id: Int
|
||||||
|
property: String
|
||||||
|
expression: String
|
||||||
|
}
|
||||||
|
|
||||||
|
input AlertMatchInput {
|
||||||
|
property: String!
|
||||||
|
expression: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
type AlertStash {
|
||||||
|
id: Int
|
||||||
|
name: String
|
||||||
|
slug: String
|
||||||
|
isPrimary: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type Alert {
|
||||||
|
id: Int
|
||||||
|
notify: Boolean
|
||||||
|
email: Boolean
|
||||||
|
isFromPreset: Boolean
|
||||||
|
isPrimary: Boolean
|
||||||
|
createdAt: Date
|
||||||
|
and: AlertAnd
|
||||||
|
actors: [AlertActor]
|
||||||
|
tags: [AlertTag]
|
||||||
|
entities: [AlertEntity]
|
||||||
|
matches: [AlertMatch]
|
||||||
|
stashes: [AlertStash]
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export async function fetchAlertsApi(req, res) {
|
export async function fetchAlertsApi(req, res) {
|
||||||
const alerts = await fetchAlerts(req.user);
|
const alerts = await fetchAlerts(req.user);
|
||||||
|
|
||||||
res.send(alerts);
|
res.send(alerts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchAlertsGraphql(query, req) {
|
||||||
|
const alerts = await fetchAlerts(req.user);
|
||||||
|
|
||||||
|
return alerts;
|
||||||
|
}
|
||||||
|
|
||||||
export async function createAlertApi(req, res) {
|
export async function createAlertApi(req, res) {
|
||||||
const alert = await createAlert(req.body, req.user);
|
const alert = await createAlert(req.body, req.user);
|
||||||
|
|
||||||
res.send(alert);
|
res.send(alert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function createAlertGraphql(query, req) {
|
||||||
|
console.log('CREATE ALERT', query);
|
||||||
|
const alert = await createAlert(query, req.user);
|
||||||
|
|
||||||
|
return alert;
|
||||||
|
}
|
||||||
|
|
||||||
export async function removeAlertApi(req, res) {
|
export async function removeAlertApi(req, res) {
|
||||||
await Promise.all(req.params.alertId.split(',').map(async (alertId) => removeAlert(alertId, req.user)));
|
await Promise.all(req.params.alertId.split(',').map(async (alertId) => removeAlert(alertId, req.user)));
|
||||||
|
|
||||||
|
@ -44,3 +152,13 @@ export async function updateNotificationApi(req, res) {
|
||||||
|
|
||||||
res.status(204).send();
|
res.status(204).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const router = Router();
|
||||||
|
|
||||||
|
router.get('/api/alerts', fetchAlertsApi);
|
||||||
|
router.post('/api/alerts', createAlertApi);
|
||||||
|
router.delete('/api/alerts/:alertId', removeAlertApi);
|
||||||
|
|
||||||
|
router.get('/api/users/:userId/notifications', fetchNotificationsApi);
|
||||||
|
router.patch('/api/users/:userId/notifications', updateNotificationsApi);
|
||||||
|
router.patch('/api/users/:userId/notifications/:notificationId', updateNotificationApi);
|
||||||
|
|
|
@ -46,6 +46,12 @@ import {
|
||||||
unstashMovieGraphql,
|
unstashMovieGraphql,
|
||||||
} from './stashes.js';
|
} from './stashes.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
alertsSchema,
|
||||||
|
fetchAlertsGraphql,
|
||||||
|
createAlertGraphql,
|
||||||
|
} from './alerts.js';
|
||||||
|
|
||||||
import { verifyKey } from '../auth.js';
|
import { verifyKey } from '../auth.js';
|
||||||
|
|
||||||
const schema = buildSchema(`
|
const schema = buildSchema(`
|
||||||
|
@ -64,6 +70,7 @@ const schema = buildSchema(`
|
||||||
${actorsSchema}
|
${actorsSchema}
|
||||||
${entitiesSchema}
|
${entitiesSchema}
|
||||||
${stashesSchema}
|
${stashesSchema}
|
||||||
|
${alertsSchema}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const DateTimeScalar = new GraphQLScalarType({
|
const DateTimeScalar = new GraphQLScalarType({
|
||||||
|
@ -125,7 +132,8 @@ export async function graphqlApi(req, res) {
|
||||||
entitiesById: async (query, args, info) => fetchEntitiesByIdGraphql(query, req, info),
|
entitiesById: async (query, args, info) => fetchEntitiesByIdGraphql(query, req, info),
|
||||||
stashes: async (query) => fetchUserStashesGraphql(query, req),
|
stashes: async (query) => fetchUserStashesGraphql(query, req),
|
||||||
stash: async (query) => fetchStashGraphql(query, req),
|
stash: async (query) => fetchStashGraphql(query, req),
|
||||||
// mutation
|
alerts: async (query) => fetchAlertsGraphql(query, req),
|
||||||
|
// stash mutation
|
||||||
createStash: async (query) => createStashGraphql(query, req),
|
createStash: async (query) => createStashGraphql(query, req),
|
||||||
updateStash: async (query) => updateStashGraphql(query, req),
|
updateStash: async (query) => updateStashGraphql(query, req),
|
||||||
removeStash: async (query) => removeStashGraphql(query, req),
|
removeStash: async (query) => removeStashGraphql(query, req),
|
||||||
|
@ -135,10 +143,12 @@ export async function graphqlApi(req, res) {
|
||||||
unstashActor: async (query) => unstashActorGraphql(query, req),
|
unstashActor: async (query) => unstashActorGraphql(query, req),
|
||||||
stashMovie: async (query) => stashMovieGraphql(query, req),
|
stashMovie: async (query) => stashMovieGraphql(query, req),
|
||||||
unstashMovie: async (query) => unstashMovieGraphql(query, req),
|
unstashMovie: async (query) => unstashMovieGraphql(query, req),
|
||||||
|
// alert mutation
|
||||||
|
createAlert: async (query) => createAlertGraphql(query, req),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const statusCode = data.errors?.[0]?.originalError.httpCode || 200;
|
const statusCode = data.errors?.[0]?.originalError?.httpCode || 200;
|
||||||
|
|
||||||
res.status(statusCode).send(data);
|
res.status(statusCode).send(data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,7 @@ export const scenesSchema = `
|
||||||
photos: [Media!]!
|
photos: [Media!]!
|
||||||
covers: [Media!]!
|
covers: [Media!]!
|
||||||
movies: [Release!]!
|
movies: [Release!]!
|
||||||
|
stashes: [Stash!]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tag {
|
type Tag {
|
||||||
|
|
|
@ -38,15 +38,7 @@ import {
|
||||||
|
|
||||||
import { router as userRouter } from './users.js';
|
import { router as userRouter } from './users.js';
|
||||||
import { router as stashesRouter } from './stashes.js';
|
import { router as stashesRouter } from './stashes.js';
|
||||||
|
import { router as alertsRouter } from './alerts.js';
|
||||||
import {
|
|
||||||
fetchAlertsApi,
|
|
||||||
createAlertApi,
|
|
||||||
removeAlertApi,
|
|
||||||
fetchNotificationsApi,
|
|
||||||
updateNotificationApi,
|
|
||||||
updateNotificationsApi,
|
|
||||||
} from './alerts.js';
|
|
||||||
|
|
||||||
import initLogger from '../logger.js';
|
import initLogger from '../logger.js';
|
||||||
|
|
||||||
|
@ -140,23 +132,15 @@ export default async function initServer() {
|
||||||
// USERS
|
// USERS
|
||||||
router.post('/api/users', signupApi);
|
router.post('/api/users', signupApi);
|
||||||
|
|
||||||
router.get('/api/users/:userId/notifications', fetchNotificationsApi);
|
|
||||||
router.patch('/api/users/:userId/notifications', updateNotificationsApi);
|
|
||||||
router.patch('/api/users/:userId/notifications/:notificationId', updateNotificationApi);
|
|
||||||
|
|
||||||
// API KEYS
|
// API KEYS
|
||||||
router.get('/api/me/keys', fetchUserKeysApi);
|
router.get('/api/me/keys', fetchUserKeysApi);
|
||||||
router.post('/api/me/keys', createKeyApi);
|
router.post('/api/me/keys', createKeyApi);
|
||||||
router.delete('/api/me/keys/:keyIdentifier', removeUserKeyApi);
|
router.delete('/api/me/keys/:keyIdentifier', removeUserKeyApi);
|
||||||
router.delete('/api/me/keys', flushUserKeysApi);
|
router.delete('/api/me/keys', flushUserKeysApi);
|
||||||
|
|
||||||
// ALERTS
|
|
||||||
router.get('/api/alerts', fetchAlertsApi);
|
|
||||||
router.post('/api/alerts', createAlertApi);
|
|
||||||
router.delete('/api/alerts/:alertId', removeAlertApi);
|
|
||||||
|
|
||||||
router.use(userRouter);
|
router.use(userRouter);
|
||||||
router.use(stashesRouter);
|
router.use(stashesRouter);
|
||||||
|
router.use(alertsRouter);
|
||||||
router.use(scenesRouter);
|
router.use(scenesRouter);
|
||||||
router.use(actorsRouter);
|
router.use(actorsRouter);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue