Change models, new migrations, fix front and make tests work

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2018-01-13 23:33:03 +01:00
parent 92d2045735
commit 20cd1bb579
186 changed files with 2982 additions and 3214 deletions

View File

@ -27,5 +27,11 @@ config :logger, :console,
import_config "#{Mix.env}.exs"
config :eventos, EventosWeb.Guardian,
issuer: "Eventos",
secret_key: "ty0WM7YBE3ojvxoUQxo8AERrNpfbXnIJ82ovkPdqbUFw31T5LcK8wGjaOiReVQjo"
issuer: "eventos",
secret_key: "ty0WM7YBE3ojvxoUQxo8AERrNpfbXnIJ82ovkPdqbUFw31T5LcK8wGjaOiReVQjo"
config :guardian, Guardian.DB,
repo: Eventos.Repo,
schema_name: "guardian_tokens", # default
token_types: ["refresh_token"], # store all token types if not set
sweep_interval: 60 # default: 60 minutes

View File

@ -50,6 +50,7 @@ config :phoenix, :stacktrace_depth, 20
# Configure your database
config :eventos, Eventos.Repo,
adapter: Ecto.Adapters.Postgres,
types: Eventos.PostgresTypes,
username: "elixir",
password: "elixir",
database: "eventos_dev",

View File

@ -7,7 +7,10 @@ config :eventos, EventosWeb.Endpoint,
server: false
# Print only warnings and errors during test
config :logger, level: :warn
config :logger,
backends: [:console],
compile_time_purge_level: :debug,
level: :info
# Configure your database
config :eventos, Eventos.Repo,
@ -16,4 +19,5 @@ config :eventos, Eventos.Repo,
password: "elixir",
database: "eventos_test",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox
pool: Ecto.Adapters.SQL.Sandbox,
types: Eventos.PostgresTypes

View File

@ -4,7 +4,7 @@ var path = require('path')
module.exports = {
build: {
env: require('./prod.env'),
index: path.resolve(__dirname, '../../lib/eventos_web/templates/app/index.html.eex'),
index: path.resolve(__dirname, '../../lib/eventos_web/templates/page/index.html.eex'),
assetsRoot: path.resolve(__dirname, '../../priv/static'),
assetsSubDirectory: '',
assetsPublicPath: '/',

View File

@ -4,11 +4,11 @@
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet">
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBF37pw38j0giICt73TCAPNogc07Upe_Q4&libraries=places"></script>
<meta charset="utf-8">
<title>libre-event</title>
<title>Eventos</title>
</head>
<body>
<noscript>
Mets du JS.
You need to activate your JS doug.
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->

6
js/package-lock.json generated
View File

@ -13648,9 +13648,9 @@
}
},
"vuetify": {
"version": "0.17.6",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-0.17.6.tgz",
"integrity": "sha512-geIGnXjYEhUn1dr+g8FCFqvV6xUbALXNJ3UZQ32T0SJdGJh/JLsScINTxV+Pxt1F36s+YRnuFyHLiFOSnuVzjA=="
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-1.0.0-beta.2.tgz",
"integrity": "sha512-eM61PPNwM0lXYaUzY0MXA7sp/N4wqxlEMcALsOBneLObVS+OgpX4Zszb7HqAOSVXipNRSP8X5ZV5TgbZscly1Q=="
},
"vuetify-google-autocomplete": {
"version": "1.1.0",

View File

@ -21,7 +21,7 @@
"vue-markdown": "^2.2.4",
"vue-router": "^3.0.1",
"vue2-google-maps": "^0.8.4",
"vuetify": "^0.17.6",
"vuetify": "^1.0.0-beta.2",
"vuetify-google-autocomplete": "^1.1.0",
"vuex": "^2.5.0",
"vuex-i18n": "1.8.0"

View File

@ -76,7 +76,6 @@
<script>
import auth from '@/auth/index';
import NavBar from '@/components/NavBar';
export default {
@ -105,15 +104,7 @@ export default {
show_new_event_button: false,
};
},
created() {
this.checkAuthMethod();
},
methods: {
checkAuthMethod() {
if (auth.checkAuth(this.$store)) {
this.show_new_event_button = true;
}
},
showMenuItem(elem) {
return elem !== null && this.$store.state.user && this.$store.state.user.roles !== undefined ? this.$store.state.user.roles.includes(elem) : true;
},

View File

@ -1,6 +1,6 @@
import { API_HOST, API_PATH } from './_entrypoint';
const jsonLdMimeType = 'application/ld+json';
const jsonLdMimeType = 'application/json';
export default function eventFetch(url, store, optionsarg = {}) {
const options = optionsarg;

View File

@ -1,87 +1,61 @@
import router from '../router/index';
import { API_HOST, API_PATH } from '../api/_entrypoint';
// URL and endpoint constants
const LOGIN_URL = `${API_HOST}${API_PATH}/login`;
const SIGNUP_URL = `${API_HOST}${API_PATH}/users/`;
const CHECK_AUTH = `${API_HOST}${API_PATH}/users/`;
const CHECK_AUTH = `${API_HOST}${API_PATH}/user/`;
const REFRESH_TOKEN = `${API_HOST}${API_PATH}/token/refresh`;
function AuthError(field, message) {
this.field = field;
this.message = message;
}
AuthError.prototype.toString = function AuthErrorToString() {
return `AuthError: ${this.message}`;
};
export default {
// User object will let us check authentication status
user: false,
authenticated: false,
token: false,
// Send a request to the login URL and save the returned JWT
login(creds, $store, redirect, error) {
login(creds, success, error) {
fetch(LOGIN_URL, { method: 'POST', body: creds, headers: { 'Content-Type': 'application/json' } })
.then(response => response.json())
.then((data) => {
if (data.code >= 300) {
throw new AuthError(null, data.message);
.then((response) => {
if (response.status === 200) {
return response.json();
}
$store.commit('LOGIN_USER');
throw response.json();
})
.then((data) => {
localStorage.setItem('token', data.token);
localStorage.setItem('refresh_token', data.refresh_token);
this.getUser(
$store,
() => router.push(redirect)
);
}).catch((err) => {
error(err);
});
// localStorage.setItem('refresh_token', data.refresh_token);
return success(data);
})
.catch(err => error(err));
},
signup(creds, $store, redirect, error) {
signup(creds, success, error) {
fetch(SIGNUP_URL, { method: 'POST', body: creds, headers: { 'Content-Type': 'application/json' } })
.then(response => response.json())
.then((response) => {
if (response.status === 200) {
return response.json();
}
throw response.json();
})
.then((data) => {
if (data.error) {
throw new AuthError(data.error.field, data.error.message);
}
$store.commit('LOGIN_USER');
localStorage.setItem('token', data.token);
localStorage.setItem('refresh_token', data.refresh_token);
// localStorage.setItem('refresh_token', data.refresh_token);
if (redirect) {
router.push(redirect);
}
}).catch((err) => {
error(err);
});
return success(data);
}).catch(err => error(err));
},
refreshToken(store, successHandler, errorHandler) {
const refreshToken = localStorage.getItem('refresh_token');
console.log("We are refreshing the jwt token");
fetch(REFRESH_TOKEN, { method: 'POST', body: JSON.stringify({refresh_token: refreshToken}), headers: { 'Content-Type': 'application/json' }})
console.log('We are refreshing the jwt token');
fetch(REFRESH_TOKEN, { method: 'POST', body: JSON.stringify({ refresh_token: refreshToken }), headers: { 'Content-Type': 'application/json' } })
.then((response) => {
if (response.ok) {
return response.json();
} else {
errorHandler('Error while authenticating');
}
return errorHandler('Error while authenticating');
})
.then((response) => {
console.log("We have a new token");
console.log('We have a new token');
this.authenticated = true;
store.commit('LOGIN_USER', response);
localStorage.setItem('token', response.token);
console.log("Let's try to auth again");
this.getUser(store, successHandler, errorHandler);
successHandler();
});
},
@ -114,19 +88,8 @@ export default {
return expirationDate < new Date();
},
checkAuth(store = null) {
const token = localStorage.getItem('token');
if (store && token) {
this.getUser(store,() => null, () => null);
}
/* if (!!token && store && !this.isTokenExpired(token)) {
this.refreshToken(store, () => null, () => null);
} */
return !!token;
},
getUser(store, successHandler, errorHandler) {
console.log("We are checking the auth");
console.log('We are checking the auth');
this.token = localStorage.getItem('token');
const options = {};
options.headers = new Headers();
@ -135,16 +98,14 @@ export default {
.then((response) => {
if (response.ok) {
return response.json();
} else {
errorHandler('Error while authenticating');
}
})
.then((response) => {
return errorHandler('Error while authenticating');
}).then((response) => {
this.authenticated = true;
console.log(response);
store.commit('SAVE_USER', response);
successHandler();
});
store.commit('LOAD_USER', response.data);
return successHandler();
});
},
// The object to be passed as a header for authenticated requests

View File

@ -29,11 +29,16 @@
:src="account.avatarRemoteUrl"
>
</v-avatar>
<v-card-title class="pl-5 pt-5">
<div class="display-1 pl-5 pt-5">@{{ account.username }}<span v-if="account.server">@{{ account.server.address }}</span></div>
</v-card-title>
<v-card-text v-if="account.description" v-html="account.description"></v-card-text>
</div>
<v-container fluid grid-list-lg>
<v-layout row>
<v-flex xs7>
<div class="headline">{{ account.display_name }}</div>
<div><span class="subheading">@{{ account.username }}</span><span v-if="account.server">@{{ account.server.address }}</span></div>
<v-card-text v-if="account.description" v-html="account.description"></v-card-text>
</v-flex>
</v-layout>
</v-container>
</v-layout>
<v-list three-line>
<v-list-tile>
@ -69,7 +74,7 @@
</v-list-tile-content>
</v-list-tile>
</v-list>
<v-container fluid grid-list-md v-if="account.participatingEvents.length > 0">
<v-container fluid grid-list-md v-if="account.participatingEvents && account.participatingEvents.length > 0">
<v-subheader>Participated at</v-subheader>
<v-layout row wrap>
<v-flex v-for="event in account.participatingEvents" :key="event.id">
@ -110,7 +115,7 @@
</v-flex>
</v-layout>
</v-container>
<v-container fluid grid-list-md v-if="account.organizingEvents.length > 0">
<v-container fluid grid-list-md v-if="account.organizingEvents && account.organizingEvents.length > 0">
<v-subheader>Organized events</v-subheader>
<v-layout row wrap>
<v-flex v-for="event in account.organizingEvents" :key="event.id">
@ -178,11 +183,12 @@ export default {
},
methods: {
fetchData() {
eventFetch('/accounts/' + this.id, this.$store)
eventFetch(`/accounts/${this.id}`, this.$store)
.then(response => response.json())
.then((response) => {
this.account = response;
this.account = response.data;
this.loading = false;
console.log(this.account);
})
}
}

View File

@ -4,11 +4,10 @@
<v-form>
<v-text-field
label="Name of the category"
v-model="category.name"
v-model="category.title"
:counter="100"
required
></v-text-field>
<input type="file" @change="processFile($event.target)">
</v-form>
<v-btn color="primary" @click="create">Create category</v-btn>
</div>
@ -22,32 +21,20 @@
data() {
return {
category: {
name: '',
imageDataUri: null,
title: '',
},
};
},
methods: {
create() {
const router = this.$router;
eventFetch('/categories', this.$store, { method: 'POST', body: JSON.stringify(this.category) })
eventFetch('/categories', this.$store, { method: 'POST', body: JSON.stringify({ category: this.category }) })
.then(response => response.json())
.then(() => {
this.loading = false;
router.push('/category')
});
},
processFile(target) {
const reader = new FileReader();
const file = target.files[0];
reader.addEventListener('load', () => {
this.category.imageDataUri = reader.result;
});
if (file) {
reader.readAsDataURL(file);
}
}
},
};
</script>

View File

@ -11,7 +11,7 @@
</v-card-media>
<v-card-title primary-title>
<div>
<h3 class="headline mb-0">{{ category.name }}</h3>
<h3 class="headline mb-0">{{ category.title }}</h3>
<div>{{ category.description }}</div>
</div>
</v-card-title>
@ -49,9 +49,9 @@
fetchData() {
eventFetch('/categories', this.$store)
.then(response => response.json())
.then((data) => {
.then((response) => {
this.loading = false;
this.categories = data['hydra:member'];
this.categories = response.data;
});
},
deleteCategory(categoryId) {

View File

@ -36,7 +36,7 @@
<v-select
v-bind:items="categories"
v-model="event.category"
item-text="name"
item-text="title"
item-value="@id"
label="Categories"
single-line
@ -62,7 +62,7 @@
<v-stepper-step step="2" :complete="e1 > 2">Date and place</v-stepper-step>
<v-stepper-content step="2">
Event starts at:
<v-text-field type="datetime-local" v-model="event.startDate"></v-text-field>
<v-text-field type="datetime-local" v-model="event.begins_on"></v-text-field>
<!--<v-layout row wrap>
<v-flex md6>
<v-dialog
@ -113,7 +113,7 @@
</v-flex>
</v-layout>-->
Event ends at:
<v-text-field type="datetime-local" v-model="event.endDate"></v-text-field>
<v-text-field type="datetime-local" v-model="event.ends_on"></v-text-field>
<!--<v-layout row wrap>
<v-flex md6>
<v-dialog
@ -216,8 +216,8 @@
event: {
title: '',
description: '',
startDate: new Date(),
endDate: new Date(),
begins_on: new Date(),
ends_on: new Date(),
seats: 0,
address: {
description: null,
@ -261,12 +261,12 @@
// '@type': 'Tag',
});
});
this.event.organizer = "/accounts/" + this.$store.state.user.account.id;
this.event.participants = ["/accounts/" + this.$store.state.user.account.id];
this.event.organizer_id = this.$store.state.user.account.id;
this.event.participants = [this.$store.state.user.account.id];
this.event.price = parseFloat(this.event.price);
if (this.id === undefined) {
eventFetch('/events', this.$store, {method: 'POST', body: JSON.stringify(this.event)})
eventFetch('/events', this.$store, {method: 'POST', body: JSON.stringify({ event: this.event })})
.then(response => response.json())
.then((data) => {
this.loading = false;
@ -284,17 +284,17 @@
fetchCategories() {
eventFetch('/categories', this.$store)
.then(response => response.json())
.then((data) => {
.then((response) => {
this.loading = false;
this.categories = data['hydra:member'];
this.categories = response.data;
});
},
fetchTags() {
eventFetch('/tags', this.$store)
.then(response => response.json())
.then((data) => {
.then((response) => {
this.loading = false;
data['hydra:member'].forEach((tag) => {
response.data.forEach((tag) => {
this.tagsFetched.push(tag.name);
});
});

View File

@ -26,7 +26,7 @@
<div class="headline">{{ event.title }}</div>
</v-card-title>
<v-container>
<span class="grey--text">{{ event.startDate | formatDate }} à <router-link :to="{name: 'EventList', params: {location: geocode(event.address.geo.latitude, event.address.geo.longitude, 10) }}">{{ event.address.addressLocality }}</router-link></span><br>
<!--<span class="grey&#45;&#45;text">{{ event.startDate | formatDate }} à <router-link :to="{name: 'EventList', params: {location: geocode(event.address.geo.latitude, event.address.geo.longitude, 10) }}">{{ event.address.addressLocality }}</router-link></span><br>-->
<p><vue-markdown>{{ event.description }}</vue-markdown></p>
<p v-if="event.organizer">Organisé par <router-link :to="{name: 'Account', params: {'id': event.organizer.id}}">{{ event.organizer.username }}</router-link></p>
</v-container>
@ -93,9 +93,9 @@
this.locationChip = true;
eventFetch(queryString, this.$store)
.then(response => response.json())
.then((data) => {
.then((response) => {
this.loading = false;
this.events = data['hydra:member'];
this.events = response.data;
});
},
deleteEvent(id) {

View File

@ -98,7 +98,7 @@
.then(response => response.json())
.then((data) => {
this.loading = false;
this.categories = data['hydra:member'];
this.categories = data;
});
},
getAddressData: function (addressData) {

View File

@ -58,7 +58,7 @@
.then(response => response.json())
.then((data) => {
this.loading = false;
this.groups = data['hydra:member'];
this.groups = data;
});
},
deleteEvent(id) {

View File

@ -1,6 +1,6 @@
<template>
<v-container>
<h1 class="welcome" v-if="$store.state.user">{{ $t("home.welcome", { 'username': $store.state.user.username}) }}</h1>
<h1 class="welcome" v-if="$store.state.user">{{ $t("home.welcome", { 'username': this.displayed_name }) }}</h1>
<h1 class="welcome" v-else>{{ $t("home.welcome_off", { 'username': $store.state.user.username}) }}</h1>
<router-link :to="{ name: 'EventList' }">{{ $t('home.events') }}</router-link>
<router-link v-if="$store.state.user === false" :to="{ name: 'Login' }">{{ $t('home.login') }}</router-link>
@ -48,12 +48,17 @@ export default {
mounted() {
// this.fetchLocations();
},
computed: {
displayed_name: function() {
return this.$store.state.user.account.display_name === null ? this.$store.state.user.account.username : this.$store.state.user.account.display_name
},
},
methods: {
fetchLocations() {
eventFetch('/locations', this.$store)
.then((response) => (response.json()))
.then((response) => {
this.locations = response['hydra:member'];
this.locations = response;
});
},
geoLocalize() {

View File

@ -65,10 +65,19 @@
methods: {
loginAction(e) {
e.preventDefault();
auth.login(JSON.stringify(this.credentials), this.$store, '/', (error) => {
this.error.show = true;
this.error.text = error.message;
this.error.field[error.field] = true;
auth.login(JSON.stringify(this.credentials), (data) => {
this.$store.commit('LOGIN_USER', data.user);
this.$router.push({ name: 'Home' });
}, (error) => {
Promise.resolve(error).then((errorMsg) => {
console.log(errorMsg);
this.error.show = true;
this.error.text = this.$t(errorMsg.display_error);
}).catch((e) => {
console.log(e);
this.error.show = true;
this.error.text = e.message;
});
});
},
},

View File

@ -58,7 +58,7 @@
</v-card-actions>
</v-card>
</v-menu>
<v-btn flat @click="$router.push({name: 'Account', params: {'id': getUser().account.id}})" v-if="$store.state.isLogged && getUser()">{{ getUser().username }}</v-btn>
<v-btn flat @click="$router.push({name: 'Account', params: {'id': getUser().account.id}})" v-if="$store.state.user">{{ this.displayed_name }}</v-btn>
</v-toolbar>
</template>
@ -95,6 +95,11 @@
}
}
},
computed: {
displayed_name: function() {
return this.$store.state.user.account.display_name === null ? this.$store.state.user.account.username : this.$store.state.user.account.display_name
},
},
methods: {
getUser() {
return this.$store.state.user === undefined ? false : this.$store.state.user;

View File

@ -72,7 +72,11 @@
methods: {
registerAction(e) {
e.preventDefault();
auth.signup(JSON.stringify(this.credentials), this.$store, {name: 'Home'}, (error) => {
auth.signup(JSON.stringify(this.credentials), (response) => {
console.log(response);
this.$store.commit('LOGIN_USER', response.user);
this.$router.push({ name: 'Home' });
}, (error) => {
this.error.show = true;
this.error.text = error.message;
this.error.field[error.field] = true;

View File

@ -12,4 +12,9 @@ export default {
title: "Votre liste d'événements",
},
},
session: {
error: {
bad_login: 'Erreur lors de la connexion : Votre nom d\'utilisateur ou votre mot de passe est incorrect',
},
},
};

View File

@ -10,8 +10,9 @@ import VuexI18n from 'vuex-i18n';
import 'vuetify/dist/vuetify.min.css';
import App from '@/App';
import router from '@/router';
import storeData from './store/index';
import translations from './i18n/index';
import storeData from '@/store/index';
import translations from '@/i18n/index';
import auth from '@/auth';
Vue.config.productionTip = false;
@ -46,6 +47,19 @@ Object.entries(translations).forEach((key) => {
Vue.i18n.set(language);
Vue.i18n.fallback('en');
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiredAuth) && store.state.user === undefined || store.state.user == null) {
next({
name: 'Login',
query: { redirect: to.fullPath }
});
} else {
next();
}
});
auth.getUser(store, () => {}, () => {});
/* eslint-disable no-new */
new Vue({
el: '#app',

View File

@ -14,7 +14,6 @@ import Account from '@/components/Account/Account';
import CreateGroup from '@/components/Group/Create';
import Group from '@/components/Group/Group';
import GroupList from '@/components/Group/GroupList';
import Auth from '@/auth/index';
Vue.use(Router);
@ -118,16 +117,4 @@ const router = new Router({
],
});
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiredAuth) && !Auth.checkAuth()) {
console.log('needs login');
next({
path: '/',
query: { redirect: to.fullPath }
});
} else {
next();
}
});
export default router;

View File

@ -1,21 +1,26 @@
import { LOGIN_USER, LOGOUT_USER, SAVE_USER } from './mutation-types';
import { LOGIN_USER, LOGOUT_USER, LOAD_USER } from './mutation-types';
const state = {
isLogged: !!localStorage.getItem('token'),
user: false,
};
/* eslint-disable */
const mutations = {
[LOGIN_USER](state) {
[LOGIN_USER](state, user) {
state.isLogged = true;
state.user = user;
},
[LOAD_USER](state, user) {
state.user = user;
},
[LOGOUT_USER](state) {
state.isLogged = false;
},
[SAVE_USER](state, user) {
state.user = user;
state.user = null;
},
};
/* eslint-enable */
export default { state, mutations };

View File

@ -1,3 +1,3 @@
export const LOGIN_USER = 'LOGIN_USER';
export const LOAD_USER = 'LOAD_USER';
export const LOGOUT_USER = 'LOGOUT_USER';
export const SAVE_USER = 'SAVE_USER';

View File

@ -1,22 +1,23 @@
defmodule Eventos.Accounts.Account do
use Ecto.Schema
import Ecto.Changeset
alias Eventos.Accounts.{Account, GroupAccount, GroupRequest, Group, User}
alias Eventos.Accounts.{Account, User}
alias Eventos.Groups.{Group, Member, Request}
alias Eventos.Events.Event
schema "accounts" do
field :username, :string
field :description, :string
field :display_name, :string
field :domain, :string, default: nil
field :domain, :string
field :private_key, :string
field :public_key, :string
field :suspended, :boolean, default: false
field :uri, :string
field :url, :string
has_many :organized_events, Event
many_to_many :groups, Group, join_through: GroupAccount
has_many :group_request, GroupRequest
field :username, :string
has_many :organized_events, Event, [foreign_key: :organizer_id]
many_to_many :groups, Group, join_through: Member
has_many :group_request, Request
has_one :user, User
timestamps()
@ -26,7 +27,14 @@ defmodule Eventos.Accounts.Account do
def changeset(%Account{} = account, attrs) do
account
|> cast(attrs, [:username, :domain, :display_name, :description, :private_key, :public_key, :suspended, :uri, :url])
|> validate_required([:username, :display_name, :description, :private_key, :public_key, :suspended])
|> validate_required([:username, :public_key, :suspended, :uri, :url])
|> unique_constraint(:username, name: :accounts_username_domain_index)
end
def registration_changeset(%Account{} = account, attrs) do
account
|> cast(attrs, [:username, :domain, :display_name, :description, :private_key, :public_key, :suspended, :uri, :url])
|> validate_required([:username, :public_key, :suspended, :uri, :url])
|> unique_constraint(:username)
end
end

View File

@ -4,130 +4,8 @@ defmodule Eventos.Accounts do
"""
import Ecto.Query, warn: false
import Logger
alias Eventos.Repo
alias Eventos.Accounts.User
@doc """
Returns the list of users.
## Examples
iex> list_users()
[%User{}, ...]
"""
def list_users do
Repo.all(User)
end
@doc """
Gets a single user.
Raises `Ecto.NoResultsError` if the User does not exist.
## Examples
iex> get_user!(123)
%User{}
iex> get_user!(456)
** (Ecto.NoResultsError)
"""
def get_user!(id), do: Repo.get!(User, id)
@doc """
Get an user by email
"""
def find(email) do
Repo.get_by!(User, email: email)
end
@doc """
Authenticate user
"""
def authenticate(%{user: user, password: password}) do
# Does password match the one stored in the database?
Logger.debug(user.password_hash)
Logger.debug(password)
case Comeonin.Argon2.checkpw(password, user.password_hash) do
true ->
# Yes, create and return the token
EventosWeb.Guardian.encode_and_sign(user)
_ ->
# No, return an error
{:error, :unauthorized}
end
end
@doc """
Creates a user.
## Examples
iex> create_user(%{field: value})
{:ok, %User{}}
iex> create_user(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_user(attrs \\ %{}) do
%User{}
|> User.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a user.
## Examples
iex> update_user(user, %{field: new_value})
{:ok, %User{}}
iex> update_user(user, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_user(%User{} = user, attrs) do
user
|> User.changeset(attrs)
|> Repo.update()
end
@doc """
Deletes a User.
## Examples
iex> delete_user(user)
{:ok, %User{}}
iex> delete_user(user)
{:error, %Ecto.Changeset{}}
"""
def delete_user(%User{} = user) do
Repo.delete(user)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking user changes.
## Examples
iex> change_user(user)
%Ecto.Changeset{source: %User{}}
"""
def change_user(%User{} = user) do
User.changeset(user, %{})
end
import Logger
alias Eventos.Accounts.Account
@ -158,7 +36,14 @@ defmodule Eventos.Accounts do
** (Ecto.NoResultsError)
"""
def get_account!(id), do: Repo.get!(Account, id)
def get_account!(id) do
account = Repo.get!(Account, id)
end
def get_account_with_everything!(id) do
account = Repo.get!(Account, id)
|> Repo.preload :organized_events
end
@doc """
Creates a account.
@ -225,291 +110,177 @@ defmodule Eventos.Accounts do
Account.changeset(account, %{})
end
alias Eventos.Accounts.Group
alias Eventos.Accounts.User
@doc """
Returns the list of groups.
Returns the list of users.
## Examples
iex> list_groups()
[%Group{}, ...]
iex> list_users()
[%User{}, ...]
"""
def list_groups do
Repo.all(Group)
def list_users do
Repo.all(User)
end
def list_users_with_accounts do
Repo.all(User)
|> Repo.preload :account
end
@doc """
Gets a single group.
Gets a single user.
Raises `Ecto.NoResultsError` if the Group does not exist.
Raises `Ecto.NoResultsError` if the User does not exist.
## Examples
iex> get_group!(123)
%Group{}
iex> get_user!(123)
%User{}
iex> get_group!(456)
iex> get_user!(456)
** (Ecto.NoResultsError)
"""
def get_group!(id), do: Repo.get!(Group, id)
def get_user!(id), do: Repo.get!(User, id)
def get_user_with_account!(id) do
Repo.get!(User, id)
|> Repo.preload :account
end
@doc """
Creates a group.
Get an user by email
"""
def find_by_email(email) do
user = Repo.get_by(User, email: email)
|> Repo.preload :account
end
@doc """
Authenticate user
"""
def authenticate(%{user: user, password: password}) do
# Does password match the one stored in the database?
case Comeonin.Argon2.checkpw(password, user.password_hash) do
true ->
# Yes, create and return the token
EventosWeb.Guardian.encode_and_sign(user)
_ ->
# No, return an error
{:error, :unauthorized}
end
end
@doc """
Register user
"""
def register(%{email: email, password: password, username: username}) do
{:ok, {privkey, pubkey}} = RsaEx.generate_keypair("4096")
account = Eventos.Accounts.Account.registration_changeset(%Eventos.Accounts.Account{}, %{
username: username,
domain: nil,
private_key: privkey,
public_key: pubkey,
uri: "h",
url: "h"
})
user = Eventos.Accounts.User.registration_changeset(%Eventos.Accounts.User{}, %{
email: email,
password: password
})
account_with_user = Ecto.Changeset.put_assoc(account, :user, user)
try do
coucou = Eventos.Repo.insert!(account_with_user)
user = find_by_email(email)
{:ok, user}
rescue
e in Ecto.InvalidChangesetError ->
Logger.debug(inspect e)
{:error, e.changeset.changes.user.errors}
end
# with {:ok, %Account{} = account} <- create_account(%{username: username, suspended: false, domain: nil, private_key: privkey, public_key: pubkey, uri: "h", url: "h"}) do
# case create_user(%{email: email, password: password, account: account}) do
# {:ok, %User{} = user } ->
# {:ok, user}
# {:error, %Ecto.Changeset{} = changeset} ->
# {:error, changeset}
# end
# end
end
@doc """
Creates a user.
## Examples
iex> create_group(%{field: value})
{:ok, %Group{}}
iex> create_user(%{field: value})
{:ok, %User{}}
iex> create_group(%{field: bad_value})
iex> create_user(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_group(attrs \\ %{}) do
%Group{}
|> Group.changeset(attrs)
def create_user(attrs \\ %{}) do
%User{}
|> User.registration_changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a group.
Updates a user.
## Examples
iex> update_group(group, %{field: new_value})
{:ok, %Group{}}
iex> update_user(user, %{field: new_value})
{:ok, %User{}}
iex> update_group(group, %{field: bad_value})
iex> update_user(user, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_group(%Group{} = group, attrs) do
group
|> Group.changeset(attrs)
def update_user(%User{} = user, attrs) do
user
|> User.changeset(attrs)
|> Repo.update()
end
@doc """
Deletes a Group.
Deletes a User.
## Examples
iex> delete_group(group)
{:ok, %Group{}}
iex> delete_user(user)
{:ok, %User{}}
iex> delete_group(group)
iex> delete_user(user)
{:error, %Ecto.Changeset{}}
"""
def delete_group(%Group{} = group) do
Repo.delete(group)
def delete_user(%User{} = user) do
Repo.delete(user)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking group changes.
Returns an `%Ecto.Changeset{}` for tracking user changes.
## Examples
iex> change_group(group)
%Ecto.Changeset{source: %Group{}}
iex> change_user(user)
%Ecto.Changeset{source: %User{}}
"""
def change_group(%Group{} = group) do
Group.changeset(group, %{})
end
alias Eventos.Accounts.GroupAccount
@doc """
Returns the list of group_accounts.
## Examples
iex> list_group_accounts()
[%GroupAccount{}, ...]
"""
def list_group_accounts do
Repo.all(GroupAccount)
end
@doc """
Gets a single group_account.
Raises `Ecto.NoResultsError` if the Group account does not exist.
## Examples
iex> get_group_account!(123)
%GroupAccount{}
iex> get_group_account!(456)
** (Ecto.NoResultsError)
"""
def get_group_account!(id), do: Repo.get!(GroupAccount, id)
@doc """
Creates a group_account.
## Examples
iex> create_group_account(%{field: value})
{:ok, %GroupAccount{}}
iex> create_group_account(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_group_account(attrs \\ %{}) do
%GroupAccount{}
|> GroupAccount.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a group_account.
## Examples
iex> update_group_account(group_account, %{field: new_value})
{:ok, %GroupAccount{}}
iex> update_group_account(group_account, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_group_account(%GroupAccount{} = group_account, attrs) do
group_account
|> GroupAccount.chan