Added user sign up and login.
|
@ -0,0 +1,116 @@
|
||||||
|
<template>
|
||||||
|
<form
|
||||||
|
class="login"
|
||||||
|
@submit.prevent="login"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="error"
|
||||||
|
class="feedback error"
|
||||||
|
>{{ error }}</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="success"
|
||||||
|
class="feedback success"
|
||||||
|
>Login successful, redirecting</div>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<input
|
||||||
|
v-model="username"
|
||||||
|
placeholder="Username or e-mail"
|
||||||
|
type="text"
|
||||||
|
class="input"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
|
||||||
|
<input
|
||||||
|
v-model="password"
|
||||||
|
placeholder="Password"
|
||||||
|
type="password"
|
||||||
|
class="input"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="button button-primary"
|
||||||
|
>Log in</button>
|
||||||
|
|
||||||
|
<router-link
|
||||||
|
to="/signup"
|
||||||
|
class="link link-external signup"
|
||||||
|
>Sign up</router-link>
|
||||||
|
</template>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function login() {
|
||||||
|
this.error = null;
|
||||||
|
this.success = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch('login', {
|
||||||
|
username: this.username,
|
||||||
|
password: this.password,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.success = true;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$router.replace(this.$route.query.ref || { name: 'home' });
|
||||||
|
}, 1000);
|
||||||
|
} catch (error) {
|
||||||
|
this.error = error.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
username: null,
|
||||||
|
password: null,
|
||||||
|
success: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
login,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.login {
|
||||||
|
width: 20rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
margin: 0 0 .5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback {
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: var(--error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
color: var(--shadow-strong);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin: 0 0 .25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup {
|
||||||
|
padding: .5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,126 @@
|
||||||
|
<template>
|
||||||
|
<form
|
||||||
|
class="signup"
|
||||||
|
@submit.prevent="signup"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="error"
|
||||||
|
class="feedback error"
|
||||||
|
>{{ error }}</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="success"
|
||||||
|
class="feedback success"
|
||||||
|
>Signup successful, redirecting</div>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<input
|
||||||
|
v-model="username"
|
||||||
|
placeholder="Username"
|
||||||
|
type="text"
|
||||||
|
class="input"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
|
||||||
|
<input
|
||||||
|
v-model="email"
|
||||||
|
placeholder="E-mail"
|
||||||
|
type="email"
|
||||||
|
class="input"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
|
||||||
|
<input
|
||||||
|
v-model="password"
|
||||||
|
placeholder="Password"
|
||||||
|
type="password"
|
||||||
|
class="input"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="button button-primary"
|
||||||
|
>Sign up</button>
|
||||||
|
|
||||||
|
<router-link
|
||||||
|
to="/login"
|
||||||
|
class="link link-external login"
|
||||||
|
>Log in</router-link>
|
||||||
|
</template>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function signup() {
|
||||||
|
this.error = null;
|
||||||
|
this.success = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch('signup', {
|
||||||
|
username: this.username,
|
||||||
|
email: this.email,
|
||||||
|
password: this.password,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.success = true;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$router.replace(this.$route.query.ref || { name: 'home' });
|
||||||
|
}, 1000);
|
||||||
|
} catch (error) {
|
||||||
|
this.error = error.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
username: null,
|
||||||
|
email: null,
|
||||||
|
password: null,
|
||||||
|
success: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
signup,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.signup {
|
||||||
|
width: 20rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
margin: 0 0 .5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback {
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: var(--error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
color: var(--shadow-strong);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin: 0 0 .25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login {
|
||||||
|
padding: .5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -95,11 +95,26 @@
|
||||||
<template v-slot:tooltip>
|
<template v-slot:tooltip>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<ul class="menu-items noselect">
|
<ul class="menu-items noselect">
|
||||||
<li
|
<router-link
|
||||||
class="menu-item disabled"
|
v-if="!me"
|
||||||
|
to="/login"
|
||||||
|
class="menu-item"
|
||||||
@click.stop
|
@click.stop
|
||||||
>
|
>
|
||||||
<Icon icon="enter2" />Sign in
|
<Icon icon="enter2" />Log in
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<li
|
||||||
|
v-if="me"
|
||||||
|
class="menu-username"
|
||||||
|
>{{ me.username }}</li>
|
||||||
|
|
||||||
|
<li
|
||||||
|
v-if="me"
|
||||||
|
class="menu-item"
|
||||||
|
@click.stop="$store.dispatch('logout')"
|
||||||
|
>
|
||||||
|
<Icon icon="enter2" />Log out
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li
|
<li
|
||||||
|
@ -200,6 +215,10 @@ function theme(state) {
|
||||||
return state.ui.theme;
|
return state.ui.theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function me(state) {
|
||||||
|
return state.auth.user;
|
||||||
|
}
|
||||||
|
|
||||||
function setTheme(newTheme) {
|
function setTheme(newTheme) {
|
||||||
this.$store.dispatch('setTheme', newTheme);
|
this.$store.dispatch('setTheme', newTheme);
|
||||||
}
|
}
|
||||||
|
@ -224,6 +243,7 @@ export default {
|
||||||
...mapState({
|
...mapState({
|
||||||
sfw,
|
sfw,
|
||||||
theme,
|
theme,
|
||||||
|
me,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -403,6 +423,8 @@ export default {
|
||||||
.menu-item {
|
.menu-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: .75rem 1rem .75rem .75rem;
|
padding: .75rem 1rem .75rem .75rem;
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
fill: var(--darken);
|
fill: var(--darken);
|
||||||
|
@ -428,6 +450,15 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-username {
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--shadow-strong);
|
||||||
|
font-size: .9rem;
|
||||||
|
padding: .75rem 1rem;
|
||||||
|
border-bottom: solid 1px var(--shadow-hint);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.search-compact {
|
.search-compact {
|
||||||
display: none;
|
display: none;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
.input {
|
.input {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: .5rem;
|
padding: .5rem;
|
||||||
border: solid 1px var(--shadow-weak);
|
border: solid 1px var(--shadow-hint);
|
||||||
color: var(--shadow-strong);
|
color: var(--shadow-strong);
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border: solid 1px var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
&::-webkit-calendar-picker-indicator {
|
&::-webkit-calendar-picker-indicator {
|
||||||
opacity: .5;
|
opacity: .5;
|
||||||
}
|
}
|
||||||
|
@ -20,6 +24,35 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
padding: .5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-primary {
|
||||||
|
color: var(--text-light);
|
||||||
|
background: var(--primary);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--primary-strong);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-secondary {
|
||||||
|
color: var(--primary);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--highlight-strong);
|
||||||
|
background: var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.album-toggle {
|
.album-toggle {
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|
|
@ -36,6 +36,7 @@ $breakpoint4: 1500px;
|
||||||
--female: #f0a;
|
--female: #f0a;
|
||||||
|
|
||||||
--alert: #f00;
|
--alert: #f00;
|
||||||
|
--error: #f00;
|
||||||
--warn: #fa0;
|
--warn: #fa0;
|
||||||
--success: #5c2;
|
--success: #5c2;
|
||||||
|
|
||||||
|
@ -58,6 +59,7 @@ $breakpoint4: 1500px;
|
||||||
--tile: #2a2a2a;
|
--tile: #2a2a2a;
|
||||||
|
|
||||||
--link: #dd6688;
|
--link: #dd6688;
|
||||||
|
--link-external: #48f;
|
||||||
--empty: #333;
|
--empty: #333;
|
||||||
|
|
||||||
--crease: #eaeaea;
|
--crease: #eaeaea;
|
||||||
|
|
|
@ -44,3 +44,12 @@ body {
|
||||||
fill: var(--primary);
|
fill: var(--primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
color: var(--link);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-external {
|
||||||
|
color: var(--link-external);
|
||||||
|
}
|
||||||
|
|
|
@ -39,6 +39,22 @@ async function post(endpoint, data) {
|
||||||
throw new Error(errorMsg);
|
throw new Error(errorMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function del(endpoint) {
|
||||||
|
const res = await fetch(`${config.api.url}${endpoint}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
mode: 'cors',
|
||||||
|
credentials: 'same-origin',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorMsg = await res.text();
|
||||||
|
|
||||||
|
throw new Error(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
async function graphql(query, variables = null) {
|
async function graphql(query, variables = null) {
|
||||||
const res = await fetch('/graphql', {
|
const res = await fetch('/graphql', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -67,5 +83,6 @@ async function graphql(query, variables = null) {
|
||||||
export {
|
export {
|
||||||
get,
|
get,
|
||||||
post,
|
post,
|
||||||
|
del,
|
||||||
graphql,
|
graphql,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,42 @@
|
||||||
function initAuthActions(_store, _router) {}
|
import { get, post, del } from '../api';
|
||||||
|
|
||||||
|
function initAuthActions(_store, _router) {
|
||||||
|
async function fetchMe({ commit }) {
|
||||||
|
const user = await get('/session');
|
||||||
|
|
||||||
|
commit('setUser', user);
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login({ commit }, credentials) {
|
||||||
|
const user = await post('/session', credentials);
|
||||||
|
|
||||||
|
commit('setUser', user);
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function signup({ commit }, credentials) {
|
||||||
|
const user = await post('/users', credentials);
|
||||||
|
|
||||||
|
commit('setUser', user);
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logout({ commit }) {
|
||||||
|
await del('/session');
|
||||||
|
|
||||||
|
commit('setUser', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fetchMe,
|
||||||
|
login,
|
||||||
|
logout,
|
||||||
|
signup,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default initAuthActions;
|
export default initAuthActions;
|
||||||
|
|
|
@ -1 +1,7 @@
|
||||||
export default {};
|
function setUser(state, user) {
|
||||||
|
state.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setUser,
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
async function initAuthObserver(store, _router) {
|
||||||
|
await store.dispatch('fetchMe');
|
||||||
|
}
|
||||||
|
|
||||||
|
export default initAuthObserver;
|
|
@ -1,4 +1,3 @@
|
||||||
export default {
|
export default {
|
||||||
authenticated: false,
|
|
||||||
user: null,
|
user: null,
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,6 +6,7 @@ import mitt from 'mitt';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
import initStore from './store';
|
import initStore from './store';
|
||||||
import initUiObservers from './ui/observers';
|
import initUiObservers from './ui/observers';
|
||||||
|
import initAuthObservers from './auth/observers';
|
||||||
|
|
||||||
import { formatDate, formatDuration } from './format';
|
import { formatDate, formatDuration } from './format';
|
||||||
|
|
||||||
|
@ -64,6 +65,7 @@ async function init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
initUiObservers(store, router);
|
initUiObservers(store, router);
|
||||||
|
initAuthObservers(store, router);
|
||||||
|
|
||||||
if (window.env.sfw) {
|
if (window.env.sfw) {
|
||||||
store.dispatch('setSfw', true);
|
store.dispatch('setSfw', true);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { createRouter, createWebHistory } from 'vue-router';
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
|
||||||
import Home from '../components/home/home.vue';
|
import Home from '../components/home/home.vue';
|
||||||
|
import Login from '../components/auth/login.vue';
|
||||||
|
import Signup from '../components/auth/signup.vue';
|
||||||
import Release from '../components/releases/release.vue';
|
import Release from '../components/releases/release.vue';
|
||||||
import Entity from '../components/entities/entity.vue';
|
import Entity from '../components/entities/entity.vue';
|
||||||
import Networks from '../components/networks/networks.vue';
|
import Networks from '../components/networks/networks.vue';
|
||||||
|
@ -16,6 +18,7 @@ import NotFound from '../components/errors/404.vue';
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
|
name: 'home',
|
||||||
redirect: {
|
redirect: {
|
||||||
name: 'updates',
|
name: 'updates',
|
||||||
params: {
|
params: {
|
||||||
|
@ -25,6 +28,16 @@ const routes = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'login',
|
||||||
|
component: Login,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/signup',
|
||||||
|
name: 'singup',
|
||||||
|
component: Signup,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/updates',
|
path: '/updates',
|
||||||
redirect: {
|
redirect: {
|
||||||
|
|
|
@ -105,6 +105,10 @@ exports.up = knex => Promise.resolve()
|
||||||
.inTable('entities')
|
.inTable('entities')
|
||||||
.onDelete('cascade');
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.date('date');
|
||||||
|
table.enum('date_precision', ['year', 'month', 'day', 'hour', 'minute', 'second'])
|
||||||
|
.defaultTo('year');
|
||||||
|
|
||||||
table.text('comment');
|
table.text('comment');
|
||||||
table.text('group');
|
table.text('group');
|
||||||
|
|
||||||
|
@ -980,6 +984,69 @@ exports.up = knex => Promise.resolve()
|
||||||
|
|
||||||
table.unique(['tag_id', 'chapter_id']);
|
table.unique(['tag_id', 'chapter_id']);
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.schema.createTable('users_roles', (table) => {
|
||||||
|
table.string('role')
|
||||||
|
.primary();
|
||||||
|
|
||||||
|
table.json('abilities');
|
||||||
|
}))
|
||||||
|
.then(() => knex('users_roles').insert([
|
||||||
|
{
|
||||||
|
role: 'admin',
|
||||||
|
abilities: JSON.stringify([ // serialization necessary to avoid array being interpreted as a PG array
|
||||||
|
{ subject: 'scene', action: 'create' },
|
||||||
|
{ subject: 'scene', action: 'update' },
|
||||||
|
{ subject: 'scene', action: 'delete' },
|
||||||
|
{ subject: 'actor', action: 'create' },
|
||||||
|
{ subject: 'actor', action: 'update' },
|
||||||
|
{ subject: 'actor', action: 'delete' },
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: 'editor',
|
||||||
|
abilities: JSON.stringify([ // serialization necessary to avoid array being interpreted as a PG array
|
||||||
|
{ subject: 'scene', action: 'update' },
|
||||||
|
{ subject: 'actor', action: 'update' },
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: 'user',
|
||||||
|
},
|
||||||
|
]))
|
||||||
|
.then(() => knex.schema.createTable('users', (table) => {
|
||||||
|
table.increments('id');
|
||||||
|
|
||||||
|
table.text('username')
|
||||||
|
.unique()
|
||||||
|
.notNullable();
|
||||||
|
|
||||||
|
table.text('email')
|
||||||
|
.unique()
|
||||||
|
.notNullable();
|
||||||
|
|
||||||
|
table.text('password')
|
||||||
|
.notNullable();
|
||||||
|
|
||||||
|
table.string('role')
|
||||||
|
.references('role')
|
||||||
|
.inTable('users_roles')
|
||||||
|
.defaultTo('user')
|
||||||
|
.notNullable();
|
||||||
|
|
||||||
|
table.json('abilities');
|
||||||
|
|
||||||
|
table.boolean('email_verified')
|
||||||
|
.notNullable()
|
||||||
|
.defaultTo(false);
|
||||||
|
|
||||||
|
table.boolean('identity_verified')
|
||||||
|
.notNullable()
|
||||||
|
.defaultTo(false);
|
||||||
|
|
||||||
|
table.datetime('created_at')
|
||||||
|
.notNullable()
|
||||||
|
.defaultTo(knex.fn.now());
|
||||||
|
}))
|
||||||
// SEARCH
|
// SEARCH
|
||||||
.then(() => { // eslint-disable-line arrow-body-style
|
.then(() => { // eslint-disable-line arrow-body-style
|
||||||
// allow vim fold
|
// allow vim fold
|
||||||
|
@ -1173,6 +1240,9 @@ exports.up = knex => Promise.resolve()
|
||||||
.then(() => { // eslint-disable-line arrow-body-style
|
.then(() => { // eslint-disable-line arrow-body-style
|
||||||
// allow vim fold
|
// allow vim fold
|
||||||
return knex.raw(`
|
return knex.raw(`
|
||||||
|
COMMENT ON TABLE users IS E'@omit';
|
||||||
|
COMMENT ON TABLE users_roles IS E'@omit';
|
||||||
|
|
||||||
COMMENT ON COLUMN actors.height IS E'@omit read,update,create,delete,all,many';
|
COMMENT ON COLUMN actors.height IS E'@omit read,update,create,delete,all,many';
|
||||||
COMMENT ON COLUMN actors.weight IS E'@omit read,update,create,delete,all,many';
|
COMMENT ON COLUMN actors.weight IS E'@omit read,update,create,delete,all,many';
|
||||||
COMMENT ON COLUMN actors.penis_length IS E'@omit read,update,create,delete,all,many';
|
COMMENT ON COLUMN actors.penis_length IS E'@omit read,update,create,delete,all,many';
|
||||||
|
@ -1249,6 +1319,9 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
DROP TABLE IF EXISTS entities_types CASCADE;
|
DROP TABLE IF EXISTS entities_types CASCADE;
|
||||||
DROP TABLE IF EXISTS entities CASCADE;
|
DROP TABLE IF EXISTS entities CASCADE;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS users CASCADE;
|
||||||
|
DROP TABLE IF EXISTS users_roles CASCADE;
|
||||||
|
|
||||||
DROP FUNCTION IF EXISTS search_releases;
|
DROP FUNCTION IF EXISTS search_releases;
|
||||||
DROP FUNCTION IF EXISTS search_sites;
|
DROP FUNCTION IF EXISTS search_sites;
|
||||||
DROP FUNCTION IF EXISTS search_entities;
|
DROP FUNCTION IF EXISTS search_entities;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"version": "1.184.1",
|
"version": "1.184.1",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@casl/ability": "^5.2.2",
|
||||||
"@graphile-contrib/pg-order-by-related": "^1.0.0-beta.6",
|
"@graphile-contrib/pg-order-by-related": "^1.0.0-beta.6",
|
||||||
"@graphile-contrib/pg-simplify-inflector": "^5.0.0-beta.1",
|
"@graphile-contrib/pg-simplify-inflector": "^5.0.0-beta.1",
|
||||||
"acorn": "^8.0.4",
|
"acorn": "^8.0.4",
|
||||||
|
@ -1262,6 +1263,17 @@
|
||||||
"to-fast-properties": "^2.0.0"
|
"to-fast-properties": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@casl/ability": {
|
||||||
|
"version": "5.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@casl/ability/-/ability-5.2.2.tgz",
|
||||||
|
"integrity": "sha512-A0GTDWojP72Z4HSgS0pfbtGnhQWbquhn9luAr4Uc/HnqWWib0NvmpXC4//7gsiMUiVYCoFozQ+nG1oeZuhT7Jg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@ucast/mongo2js": "^1.3.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/stalniy/casl/blob/master/BACKERS.md"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@eslint/eslintrc": {
|
"node_modules/@eslint/eslintrc": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz",
|
||||||
|
@ -1492,6 +1504,37 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@ucast/core": {
|
||||||
|
"version": "1.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ucast/core/-/core-1.8.2.tgz",
|
||||||
|
"integrity": "sha512-pc+XGjJmZkfypJIIRo38el/FUDtBXBlGQbXafWwRwInocXVwNbJ56efECKLgAQSyI7OCJFSaEeqpf3SrR3D6cw=="
|
||||||
|
},
|
||||||
|
"node_modules/@ucast/js": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ucast/js/-/js-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-sabiuYsM5VUg4EaCwlDxnqcrHPFvbZcXvBu+P/o4pqK2q046RLTdo0bM7iVCn5Ro4HpCiRv3QzxtW8epcluY1g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@ucast/core": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@ucast/mongo": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ucast/mongo/-/mongo-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-l/hc3TxjWO9inBrgM5iMCAcsIeV2DToppRlabQa5xB/6uHYtCXfm3TPaJgr8TU1OFxqPlaXEnNQhaV0sVHGsoQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@ucast/core": "^1.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@ucast/mongo2js": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ucast/mongo2js/-/mongo2js-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-KNOEs61wxo4VJkVGqwP2a03TKuLx9fLMQgW5HD8Th/mrcuP1SspS4W+kUQD+wB1AA5pOn65hzlHUw5wZBwme0Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@ucast/core": "^1.6.1",
|
||||||
|
"@ucast/js": "^3.0.0",
|
||||||
|
"@ucast/mongo": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@videojs/http-streaming": {
|
"node_modules/@videojs/http-streaming": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.2.4.tgz",
|
||||||
|
@ -16603,6 +16646,14 @@
|
||||||
"to-fast-properties": "^2.0.0"
|
"to-fast-properties": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@casl/ability": {
|
||||||
|
"version": "5.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@casl/ability/-/ability-5.2.2.tgz",
|
||||||
|
"integrity": "sha512-A0GTDWojP72Z4HSgS0pfbtGnhQWbquhn9luAr4Uc/HnqWWib0NvmpXC4//7gsiMUiVYCoFozQ+nG1oeZuhT7Jg==",
|
||||||
|
"requires": {
|
||||||
|
"@ucast/mongo2js": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@eslint/eslintrc": {
|
"@eslint/eslintrc": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz",
|
||||||
|
@ -16805,6 +16856,37 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@ucast/core": {
|
||||||
|
"version": "1.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ucast/core/-/core-1.8.2.tgz",
|
||||||
|
"integrity": "sha512-pc+XGjJmZkfypJIIRo38el/FUDtBXBlGQbXafWwRwInocXVwNbJ56efECKLgAQSyI7OCJFSaEeqpf3SrR3D6cw=="
|
||||||
|
},
|
||||||
|
"@ucast/js": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ucast/js/-/js-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-sabiuYsM5VUg4EaCwlDxnqcrHPFvbZcXvBu+P/o4pqK2q046RLTdo0bM7iVCn5Ro4HpCiRv3QzxtW8epcluY1g==",
|
||||||
|
"requires": {
|
||||||
|
"@ucast/core": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@ucast/mongo": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ucast/mongo/-/mongo-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-l/hc3TxjWO9inBrgM5iMCAcsIeV2DToppRlabQa5xB/6uHYtCXfm3TPaJgr8TU1OFxqPlaXEnNQhaV0sVHGsoQ==",
|
||||||
|
"requires": {
|
||||||
|
"@ucast/core": "^1.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@ucast/mongo2js": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ucast/mongo2js/-/mongo2js-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-KNOEs61wxo4VJkVGqwP2a03TKuLx9fLMQgW5HD8Th/mrcuP1SspS4W+kUQD+wB1AA5pOn65hzlHUw5wZBwme0Q==",
|
||||||
|
"requires": {
|
||||||
|
"@ucast/core": "^1.6.1",
|
||||||
|
"@ucast/js": "^3.0.0",
|
||||||
|
"@ucast/mongo": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@videojs/http-streaming": {
|
"@videojs/http-streaming": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.2.4.tgz",
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
"webpack-cli": "^3.3.11"
|
"webpack-cli": "^3.3.11"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@casl/ability": "^5.2.2",
|
||||||
"@graphile-contrib/pg-order-by-related": "^1.0.0-beta.6",
|
"@graphile-contrib/pg-order-by-related": "^1.0.0-beta.6",
|
||||||
"@graphile-contrib/pg-simplify-inflector": "^5.0.0-beta.1",
|
"@graphile-contrib/pg-simplify-inflector": "^5.0.0-beta.1",
|
||||||
"acorn": "^8.0.4",
|
"acorn": "^8.0.4",
|
||||||
|
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 1.4 MiB |
After Width: | Height: | Size: 2.5 MiB |
After Width: | Height: | Size: 3.6 MiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 2.6 MiB |
After Width: | Height: | Size: 2.1 MiB |
After Width: | Height: | Size: 2.7 MiB |
|
@ -770,12 +770,14 @@ const tagMedia = [
|
||||||
['enhanced-boobs', 1, 'Lela Star in "Thick"', 'julesjordan'],
|
['enhanced-boobs', 1, 'Lela Star in "Thick"', 'julesjordan'],
|
||||||
['enhanced-boobs', 18, 'Ebony Godess', 'actiongirls'],
|
['enhanced-boobs', 18, 'Ebony Godess', 'actiongirls'],
|
||||||
['enhanced-boobs', 'hunter_bryce_penthouse', 'Hunter Bryce in "On The Bed"', 'Penthouse'],
|
['enhanced-boobs', 'hunter_bryce_penthouse', 'Hunter Bryce in "On The Bed"', 'Penthouse'],
|
||||||
|
['enhanced-boobs', 'trudy_photodromm_1', 'Trudy', 'photodromm'],
|
||||||
['enhanced-boobs', 'kenzie_anne_playboy', 'Miss Kenzie Anne in "Supercharged"', 'playboy'],
|
['enhanced-boobs', 'kenzie_anne_playboy', 'Miss Kenzie Anne in "Supercharged"', 'playboy'],
|
||||||
['enhanced-boobs', 9, 'Putri Cinta', 'watch4beauty'],
|
['enhanced-boobs', 9, 'Putri Cinta', 'watch4beauty'],
|
||||||
['enhanced-boobs', 3, 'Ashly Anderson', 'passionhd'],
|
['enhanced-boobs', 3, 'Ashly Anderson', 'passionhd'],
|
||||||
['enhanced-boobs', 'charlie_atwell_photodromm', 'Charley Atwell', 'photodromm'],
|
['enhanced-boobs', 'charlie_atwell_photodromm', 'Charley Atwell', 'photodromm'],
|
||||||
['enhanced-boobs', '23d', 'Lulu Sex Bomb in "Tropical Touch"'],
|
['enhanced-boobs', '23d', 'Lulu Sex Bomb in "Tropical Touch"'],
|
||||||
['enhanced-boobs', 22, 'Sakura Sena'],
|
['enhanced-boobs', 22, 'Sakura Sena'],
|
||||||
|
['enhanced-boobs', 'mareeva_trudy_photodromm_1', 'Mareeva and Trudy', 'photodromm'],
|
||||||
['enhanced-boobs', 'shawna_lenee_inthecrack_1', 'Shawna Lenee', 'inthecrack'],
|
['enhanced-boobs', 'shawna_lenee_inthecrack_1', 'Shawna Lenee', 'inthecrack'],
|
||||||
['enhanced-boobs', 16, 'Marsha May in "Once You Go Black 7"', 'julesjordan'],
|
['enhanced-boobs', 16, 'Marsha May in "Once You Go Black 7"', 'julesjordan'],
|
||||||
['enhanced-boobs', 'azul_hermosa_pornstarslikeitbig', 'Azul Hermosa in "She Likes Rough Quickies"', 'pornstarslikeitbig'],
|
['enhanced-boobs', 'azul_hermosa_pornstarslikeitbig', 'Azul Hermosa in "She Likes Rough Quickies"', 'pornstarslikeitbig'],
|
||||||
|
@ -783,7 +785,6 @@ const tagMedia = [
|
||||||
['enhanced-boobs', 10, 'Tia Cyrus in "Titty-Fucked Yoga Goddess"', 'latinasextapes'],
|
['enhanced-boobs', 10, 'Tia Cyrus in "Titty-Fucked Yoga Goddess"', 'latinasextapes'],
|
||||||
['enhanced-boobs', 24, 'Shalina Devine in "Rumbling in the Ring, Part 2"', 'pornworld'],
|
['enhanced-boobs', 24, 'Shalina Devine in "Rumbling in the Ring, Part 2"', 'pornworld'],
|
||||||
['enhanced-boobs', 20, 'Chloe Lamour', 'ddfbusty'],
|
['enhanced-boobs', 20, 'Chloe Lamour', 'ddfbusty'],
|
||||||
['enhanced-boobs', 11, 'Jessa Rhodes and Cali Carter in "Busty Anal Workout"', 'lesbianx'],
|
|
||||||
['enhanced-boobs', 13, 'Kitana Lure', 'assholefever'],
|
['enhanced-boobs', 13, 'Kitana Lure', 'assholefever'],
|
||||||
['enhanced-boobs', 8, 'Amber Alena', 'score'],
|
['enhanced-boobs', 8, 'Amber Alena', 'score'],
|
||||||
['enhanced-boobs', 19, 'Kerrie Lee in "Bricked"', 'studio66tv'],
|
['enhanced-boobs', 19, 'Kerrie Lee in "Bricked"', 'studio66tv'],
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const util = require('util');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
const knex = require('./knex');
|
||||||
|
const { curateUser } = require('./users');
|
||||||
|
const { HttpError } = require('./errors');
|
||||||
|
|
||||||
|
const scrypt = util.promisify(crypto.scrypt);
|
||||||
|
|
||||||
|
async function verifyPassword(password, storedPassword) {
|
||||||
|
const [salt, hash] = storedPassword.split('/');
|
||||||
|
const hashedPassword = (await scrypt(password, salt, 64)).toString('hex');
|
||||||
|
|
||||||
|
if (hashedPassword === hash) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new HttpError('Username or password incorrect', 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login(credentials) {
|
||||||
|
const user = await knex('users')
|
||||||
|
.select('users.*', 'users_roles.abilities as role_abilities')
|
||||||
|
.where('username', credentials.username)
|
||||||
|
.orWhere('email', credentials.username)
|
||||||
|
.leftJoin('users_roles', 'users_roles.role', 'users.role')
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new HttpError('Username or password incorrect', 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
await verifyPassword(credentials.password, user.password);
|
||||||
|
|
||||||
|
return curateUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function signup(credentials) {
|
||||||
|
if (!credentials.username) {
|
||||||
|
throw new HttpError('Username required', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!credentials.email) {
|
||||||
|
throw new HttpError('E-mail required', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (credentials.password?.length < 3) {
|
||||||
|
throw new HttpError('Password must be 3 characters or longer', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingUser = await knex('users')
|
||||||
|
.where('username', credentials.username)
|
||||||
|
.orWhere('email', credentials.email)
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (existingUser) {
|
||||||
|
throw new HttpError('Username or e-mail already in use', 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
const salt = crypto.randomBytes(16).toString('hex');
|
||||||
|
const hashedPassword = (await scrypt(credentials.password, salt, 64)).toString('hex');
|
||||||
|
const storedPassword = `${salt}/${hashedPassword}`;
|
||||||
|
|
||||||
|
const [user] = await knex('users')
|
||||||
|
.insert({
|
||||||
|
username: credentials.username,
|
||||||
|
email: credentials.email,
|
||||||
|
password: storedPassword,
|
||||||
|
})
|
||||||
|
.returning('*');
|
||||||
|
|
||||||
|
return curateUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
login,
|
||||||
|
signup,
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const knex = require('./knex');
|
||||||
|
|
||||||
|
function curateUser(user) {
|
||||||
|
if (!user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ability = [...(user.role_abilities || []), ...(user.abilities || [])];
|
||||||
|
|
||||||
|
const curatedUser = {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
email: user.email,
|
||||||
|
emailVerified: user.email_verified,
|
||||||
|
identityVerified: user.identity_verified,
|
||||||
|
ability,
|
||||||
|
createdAt: user.created_at,
|
||||||
|
};
|
||||||
|
|
||||||
|
return curatedUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchUser(userId) {
|
||||||
|
const user = await knex('users')
|
||||||
|
.select('users.*', 'users_roles.abilities as role_abilities')
|
||||||
|
.where('id', userId)
|
||||||
|
.orWhere('username', userId)
|
||||||
|
.orWhere('email', userId)
|
||||||
|
.leftJoin('users_roles', 'users_roles.role', 'users.role')
|
||||||
|
.first();
|
||||||
|
|
||||||
|
return curateUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
curateUser,
|
||||||
|
fetchUser,
|
||||||
|
};
|
|
@ -0,0 +1,45 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { login, signup } = require('../auth');
|
||||||
|
const { fetchUser } = require('../users');
|
||||||
|
|
||||||
|
async function loginApi(req, res) {
|
||||||
|
const user = await login(req.body);
|
||||||
|
|
||||||
|
req.session.user = user;
|
||||||
|
res.send(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logoutApi(req, res) {
|
||||||
|
req.session.destroy((error) => {
|
||||||
|
if (error) {
|
||||||
|
res.status(500).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(204).send();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchMeApi(req, res) {
|
||||||
|
if (req.session.user) {
|
||||||
|
req.session.user = await fetchUser(req.session.user.id, req.session.user);
|
||||||
|
|
||||||
|
res.send(req.session.user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(401).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function signupApi(req, res) {
|
||||||
|
const user = await signup(req.body);
|
||||||
|
|
||||||
|
res.send(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
login: loginApi,
|
||||||
|
logout: logoutApi,
|
||||||
|
fetchMe: fetchMeApi,
|
||||||
|
signup: signupApi,
|
||||||
|
};
|
|
@ -15,6 +15,13 @@ const errorHandler = require('./error');
|
||||||
|
|
||||||
const pg = require('./postgraphile');
|
const pg = require('./postgraphile');
|
||||||
|
|
||||||
|
const {
|
||||||
|
login,
|
||||||
|
logout,
|
||||||
|
signup,
|
||||||
|
fetchMe,
|
||||||
|
} = require('./auth');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
fetchScene,
|
fetchScene,
|
||||||
fetchScenes,
|
fetchScenes,
|
||||||
|
@ -60,6 +67,12 @@ async function initServer() {
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/api/session', fetchMe);
|
||||||
|
router.post('/api/session', login);
|
||||||
|
router.delete('/api/session', logout);
|
||||||
|
|
||||||
|
router.post('/api/users', signup);
|
||||||
|
|
||||||
router.get('/api/scenes', fetchScenes);
|
router.get('/api/scenes', fetchScenes);
|
||||||
router.get('/api/scenes/:releaseId', fetchScene);
|
router.get('/api/scenes/:releaseId', fetchScene);
|
||||||
router.get('/api/scenes/:releaseId/poster', fetchScenePoster);
|
router.get('/api/scenes/:releaseId/poster', fetchScenePoster);
|
||||||
|
|