mirror of
https://github.com/psforever/PSFPortal.git
synced 2026-01-19 18:14:45 +00:00
Enable foreign profile view
This commit is contained in:
parent
20946fdb43
commit
cb918033a6
|
|
@ -47,7 +47,7 @@ api.post('/user/:user/add_gm', async (req, res, next) => {
|
|||
const account = req.user;
|
||||
|
||||
try {
|
||||
await db.update_account(account.id, {"gm" : true})
|
||||
await db.update_account(account.id, {[db.ACCOUNT.ADMIN] : true})
|
||||
res.status(200).json({});
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
|
|
@ -59,7 +59,7 @@ api.post('/user/:user/remove_gm', async (req, res, next) => {
|
|||
const account = req.user;
|
||||
|
||||
try {
|
||||
await db.update_account(account.id, {"gm" : false})
|
||||
await db.update_account(account.id, {[db.ACCOUNT.ADMIN] : false})
|
||||
res.status(200).json({});
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
|
|
@ -72,7 +72,7 @@ api.post('/user/:user/ban', async (req, res, next) => {
|
|||
|
||||
try {
|
||||
// also drop GM if they had it...
|
||||
await db.update_account(account.id, {"inactive" : true, "gm" : false})
|
||||
await db.update_account(account.id, {[db.ACCOUNT.BANNED] : true, [db.ACCOUNT.ADMIN] : false})
|
||||
res.status(200).json({});
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
|
|
@ -84,10 +84,9 @@ api.post('/user/:user/unban', async (req, res, next) => {
|
|||
const account = req.user;
|
||||
|
||||
try {
|
||||
await db.update_account(account.id, {"inactive" : false})
|
||||
await db.update_account(account.id, {[db.ACCOUNT.BANNED] : false})
|
||||
res.status(200).json({});
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
res.status(500).json({ message: 'error' });
|
||||
}
|
||||
|
||||
|
|
|
|||
90
api/db.js
90
api/db.js
|
|
@ -56,13 +56,59 @@ export const CHARACTER = Object.freeze({
|
|||
DELETED: Symbol("deleted"),
|
||||
});
|
||||
|
||||
export const LOGIN = Object.freeze({
|
||||
THIS: Symbol("logins"),
|
||||
ID: Symbol("id"),
|
||||
ACCOUNT_ID: Symbol("account_id"),
|
||||
});
|
||||
|
||||
function to_sql(symbol) {
|
||||
assert(typeof symbol == 'symbol')
|
||||
return String(symbol).slice(7,-1);
|
||||
}
|
||||
|
||||
async function get_row_count(table) {
|
||||
const resp = await pool.query(`SELECT COUNT(*) FROM ${to_sql(table)}`);
|
||||
function to_sql_kv(fields, idx=1) {
|
||||
let SQL = [];
|
||||
let values = [];
|
||||
|
||||
// This will ONLY get Symbols in the field dict
|
||||
assert(Object.getOwnPropertySymbols(fields).length > 0, "to_sql_kv must have at least one field")
|
||||
|
||||
Object.getOwnPropertySymbols(fields).forEach(key => {
|
||||
assert(typeof key == 'symbol')
|
||||
SQL.push(to_sql(key)+"=$"+idx++);
|
||||
values.push(fields[key]);
|
||||
});
|
||||
|
||||
return {
|
||||
sql: SQL,
|
||||
next_idx: idx,
|
||||
values: values,
|
||||
}
|
||||
}
|
||||
|
||||
function build_SET(fields, idx=1) {
|
||||
const kv = to_sql_kv(fields, idx);
|
||||
kv.sql = Symbol(kv.sql.join(", "));
|
||||
return kv;
|
||||
}
|
||||
|
||||
function build_WHERE(fields, idx=1) {
|
||||
const kv = to_sql_kv(fields, idx);
|
||||
kv.sql = Symbol(kv.sql.join(" AND "));
|
||||
return kv;
|
||||
}
|
||||
|
||||
async function get_row_count(table, filter=undefined) {
|
||||
let resp;
|
||||
|
||||
if (filter) {
|
||||
const where = build_WHERE(filter);
|
||||
resp = await pool.query(`SELECT COUNT(*) FROM ${to_sql(table)} WHERE ${to_sql(where.sql)}`,
|
||||
where.values);
|
||||
} else {
|
||||
resp = await pool.query(`SELECT COUNT(*) FROM ${to_sql(table)}`);
|
||||
}
|
||||
return parseInt(resp.rows[0].count);
|
||||
}
|
||||
|
||||
|
|
@ -70,6 +116,13 @@ export async function connect_to_db() {
|
|||
pool = new pg.Pool()
|
||||
try {
|
||||
const res = await pool.query('SELECT NOW()')
|
||||
|
||||
// Quick hack for query debugging (throws exception)
|
||||
const _query = pool.query;
|
||||
pool.query_log = (q, v) => {
|
||||
console.log("QUERY LOG: ", q, v);
|
||||
return _query(q, v);
|
||||
}
|
||||
console.log(`Connected to the psql database at ${process.env.PGHOST}`)
|
||||
} catch (e) {
|
||||
console.log("Unable to connect to the database: " + e.message);
|
||||
|
|
@ -80,9 +133,14 @@ export async function connect_to_db() {
|
|||
export async function get_account_by_id(id) {
|
||||
try {
|
||||
const account = await pool.query('SELECT * FROM accounts WHERE id=$1', [id]);
|
||||
const account_obj = account.rows[0];
|
||||
|
||||
if (account.rows.length == 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const account_obj = account.rows[0];
|
||||
delete account_obj.passhash;
|
||||
|
||||
return account_obj;
|
||||
} catch (e) {
|
||||
throw e;
|
||||
|
|
@ -222,29 +280,16 @@ export async function create_account(username, password) {
|
|||
}
|
||||
}
|
||||
|
||||
function build_set(fields, idx=1) {
|
||||
let SQL = []
|
||||
let values = []
|
||||
|
||||
// TODO: sort for consistency
|
||||
Object.keys(fields).forEach(key => {
|
||||
SQL.push(key+"=$"+idx++)
|
||||
values.push(fields[key])
|
||||
});
|
||||
|
||||
return [SQL.join(", "), idx, values]
|
||||
}
|
||||
|
||||
export async function update_account(account_id, fields) {
|
||||
if (fields === {}) {
|
||||
return
|
||||
}
|
||||
|
||||
const set = build_set(fields);
|
||||
set[2].push(account_id)
|
||||
const set = build_SET(fields);
|
||||
set.values.push(account_id)
|
||||
|
||||
try {
|
||||
const update_result = await pool.query('UPDATE accounts SET ' + set[0] + ' WHERE id=$'+set[1],set[2]);
|
||||
const update_result = await pool.query(`UPDATE accounts SET ${to_sql(set.sql)} WHERE id=$${set.next_idx}`, set.values);
|
||||
return update_result.rowCount;
|
||||
} catch (e) {
|
||||
if (e.code)
|
||||
|
|
@ -287,9 +332,10 @@ export async function get_account_logins(account_id, pagination) {
|
|||
const values = [account_id, start_id, pagination.items_per_page];
|
||||
|
||||
try {
|
||||
const logins = await pool.query('SELECT * FROM logins WHERE account_id=$1 ORDER by login_time DESC ' +
|
||||
` OFFSET $2 LIMIT $3`, values);
|
||||
pagination.item_count = 100;
|
||||
const login_count = await get_row_count(LOGIN.THIS, { [LOGIN.ACCOUNT_ID] : account_id });
|
||||
const logins = await pool.query('SELECT * FROM logins WHERE account_id=$1 ORDER by login_time DESC ' + ` OFFSET $2 LIMIT $3`, values);
|
||||
|
||||
pagination.item_count = login_count;
|
||||
pagination.page_count = Math.ceil(pagination.item_count / pagination.items_per_page);
|
||||
|
||||
return logins.rows;
|
||||
|
|
|
|||
28
api/index.js
28
api/index.js
|
|
@ -20,13 +20,6 @@ if (process.env.NODE_ENV !== "production") {
|
|||
async function sessionRequired(req, res, next) {
|
||||
if (!req.session || !req.session.account_id) {
|
||||
res.status(403).json({message: 'session required'})
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
async function adminRequired(req, res, next) {
|
||||
if (!req.session || !req.session.account_id) {
|
||||
res.status(403).json({message: 'admin required'})
|
||||
} else {
|
||||
try {
|
||||
const account = await db.get_account_by_id(req.session.account_id);
|
||||
|
|
@ -35,11 +28,8 @@ async function adminRequired(req, res, next) {
|
|||
console.log("ERROR: failed to lookup account from session!")
|
||||
res.status(500).json({message: 'error'});
|
||||
} else {
|
||||
if (account.gm === true && account.inactive === false) {
|
||||
next();
|
||||
} else {
|
||||
res.status(403).json({message : 'admin required'})
|
||||
}
|
||||
req.session_account = account;
|
||||
next();
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
|
|
@ -47,6 +37,18 @@ async function adminRequired(req, res, next) {
|
|||
}
|
||||
}
|
||||
}
|
||||
async function adminRequired(req, res, next) {
|
||||
if (!req.session_account) {
|
||||
console.log("ERROR: sessionRequired needs to be called before adminRequired")
|
||||
res.status(500).json({message: ''})
|
||||
} else {
|
||||
if (req.session_account.gm === true && req.session_account.inactive === false) {
|
||||
next();
|
||||
} else {
|
||||
res.status(403).json({message : 'admin required'})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
api.use(bodyParser.json());
|
||||
api.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
|
@ -54,7 +56,7 @@ api.use(bodyParser.urlencoded({ extended: true }));
|
|||
api.use(api_auth)
|
||||
api.use(api_info)
|
||||
api.use(sessionRequired, api_user)
|
||||
api.use(adminRequired, api_admin)
|
||||
api.use(sessionRequired, adminRequired, api_admin)
|
||||
|
||||
api.post("/bad_route", async (req, res, next) => {
|
||||
console.log("BAD APP ROUTE:", req.body.route)
|
||||
|
|
|
|||
17
api/user.js
17
api/user.js
|
|
@ -16,10 +16,17 @@ api.get('/user', async (req, res, next) => {
|
|||
}
|
||||
});
|
||||
|
||||
api.get('/user/profile', async (req, res, next) => {
|
||||
api.get('/user/:user/profile', async (req, res, next) => {
|
||||
const target_account = req.user;
|
||||
|
||||
if (target_account.id !== req.session.account_id && !req.session_account.gm) {
|
||||
res.status(403).json({ message: 'not allowed to see for other users' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const account = await db.get_account_by_id(req.session.account_id);
|
||||
const characters = await db.get_characters_by_account(req.session.account_id);
|
||||
const account = await db.get_account_by_id(target_account.id);
|
||||
const characters = await db.get_characters_by_account(target_account.id);
|
||||
|
||||
res.status(200).json({
|
||||
id : account.id,
|
||||
|
|
@ -36,19 +43,17 @@ api.get('/user/profile', async (req, res, next) => {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
api.get('/user/:user/logins', async (req, res, next) => {
|
||||
const account = req.user;
|
||||
const pagination = get_pagination(req);
|
||||
|
||||
if (account.id !== req.session.account_id) {
|
||||
if (account.id !== req.session.account_id && !req.session_account.gm) {
|
||||
res.status(403).json({ message: 'not allowed to see for other users' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const logins = await db.get_account_logins(account.id, pagination)
|
||||
console.log(logins)
|
||||
res.status(200).json({ logins : logins, page: pagination});
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,13 @@ export function get_pagination(req) {
|
|||
}
|
||||
|
||||
export async function fetch_user_middleware(req, res, next, id) {
|
||||
id = parseInt(id);
|
||||
|
||||
if (id <= 0) {
|
||||
res.status(500).json({message: 'error'});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const account = await db.get_account_by_id(id);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,11 +14,10 @@
|
|||
import { onMount } from 'svelte';
|
||||
import page from 'page';
|
||||
|
||||
import { fade } from 'svelte/transition';
|
||||
import { get_initial_state } from './UserState.js';
|
||||
|
||||
import Nav from './components/Nav.svelte'
|
||||
import Alert from './components/Alert.svelte'
|
||||
|
||||
import Home from './views/Home.svelte';
|
||||
import Login from './views/Login.svelte';
|
||||
import Register from './views/Register.svelte';
|
||||
|
|
@ -28,12 +27,12 @@ import UserList from './views/UserList.svelte';
|
|||
import AdminPanel from './views/AdminPanel.svelte';
|
||||
import CharacterList from './views/CharacterList.svelte';
|
||||
|
||||
// prevent pop-in
|
||||
// prevent view pop-in
|
||||
let initialized = false;
|
||||
|
||||
onMount(async () => {
|
||||
await get_initial_state()
|
||||
initialized = true;
|
||||
page() // start the router
|
||||
});
|
||||
|
||||
let route;
|
||||
|
|
@ -53,9 +52,18 @@ function setRoute(r, initialState) {
|
|||
if (!first)
|
||||
previousCtx = currentCtx
|
||||
|
||||
if (!first && currentCtx.route == r)
|
||||
if (!first && ctx.pathname == previousCtx.pageCtx.pathname)
|
||||
return
|
||||
|
||||
if (process.env.NODE_ENV !== "production" && !first) {
|
||||
console.log("-------------------NEW ROUTE--------------\n",
|
||||
previousCtx.pageCtx.pathname, " -> ", ctx.pathname)
|
||||
|
||||
if (previousCtx && r == previousCtx.route)
|
||||
console.log("/!\\ Re-rendering same view with different params")
|
||||
}
|
||||
|
||||
// We are changing views, clear the global app message
|
||||
if (appAlert)
|
||||
appAlert.message("");
|
||||
|
||||
|
|
@ -64,27 +72,39 @@ function setRoute(r, initialState) {
|
|||
else
|
||||
initialized = true;
|
||||
|
||||
// Change the component context
|
||||
currentCtx = {
|
||||
route : r,
|
||||
routeParams : ctx.params,
|
||||
pageCtx : ctx,
|
||||
}
|
||||
|
||||
/* If the previous view compoent was the same, we need to
|
||||
force svelte to rerender it if some of the parameters have changed.
|
||||
For example, clicking from another User's view to ourself wont lead
|
||||
to a render due to the component not changing. We force this change
|
||||
by scheduling the two changes on separate ticks.
|
||||
*/
|
||||
if (previousCtx && r == previousCtx.route) {
|
||||
setImmediate(() => currentCtx.route = null)
|
||||
setImmediate(() => currentCtx.route = r)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
page("/", setRoute(Home, true));
|
||||
page("/login", setRoute(Login, true));
|
||||
page("/register", setRoute(Register));
|
||||
page("/register", setRoute(Register));
|
||||
page("/admin", setRoute(AdminPanel));
|
||||
page("/profile", setRoute(Profile, true));
|
||||
page("/user/:id", setRoute(Profile, true));
|
||||
//page("/users", setRoute(UserList));
|
||||
//page("/characters", setRoute(CharacterList));
|
||||
page("/admin", setRoute(AdminPanel));
|
||||
//page("/recovery", setRoute(Recovery));
|
||||
page("/profile", setRoute(Profile, true));
|
||||
page("*", setRoute(BadRoute));
|
||||
page()
|
||||
</script>
|
||||
|
||||
{#if currentCtx}
|
||||
<Nav bind:route={currentCtx.pageCtx.pathname}/>
|
||||
|
||||
<main role="main" class="container">
|
||||
|
|
@ -104,3 +124,4 @@ All other trademarks or tradenames are properties of their respective owners.
|
|||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,11 @@ export async function logout() {
|
|||
page("/")
|
||||
}
|
||||
|
||||
loggedIn.subscribe((v) => {
|
||||
console.log(loggedIn, v)
|
||||
})
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
loggedIn.subscribe((v) => {
|
||||
console.log("Login state: ", v)
|
||||
})
|
||||
}
|
||||
|
||||
export async function get_initial_state() {
|
||||
try {
|
||||
|
|
@ -42,7 +44,6 @@ export async function get_initial_state() {
|
|||
if (e.response.status === 403) {
|
||||
console.log("User not logged in / not admin!")
|
||||
clear_user_state();
|
||||
return false;
|
||||
} else {
|
||||
console.log("Unknown login error", e)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
{/if}
|
||||
|
||||
{#if $isAdmin}
|
||||
<a href="/character/{character.id}">{character.name}</a>
|
||||
<a href="/user/{character.account_id}">{character.name}</a>
|
||||
{:else}
|
||||
<span>{character.name}</span>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
import { cubicOut } from 'svelte/easing';
|
||||
|
||||
const progress = tweened(0, {
|
||||
delay: 100,
|
||||
duration: 1000,
|
||||
easing: cubicOut
|
||||
});
|
||||
|
|
@ -16,21 +17,22 @@
|
|||
let tr, nc, vs;
|
||||
|
||||
onMount(() => {
|
||||
setTimeout(() => progress.set(1.0), 100);
|
||||
|
||||
tr.style.height = "1px";
|
||||
nc.style.height = "1px";
|
||||
vs.style.height = "1px";
|
||||
|
||||
progress.subscribe((v) => {
|
||||
if (!tr || !nc || !vs)
|
||||
return;
|
||||
|
||||
tr.style.height = v*percentages.TR*200 + "px";
|
||||
nc.style.height = v*percentages.NC*200 + "px";
|
||||
vs.style.height = v*percentages.VS*200 + "px";
|
||||
})
|
||||
|
||||
setTimeout(() => progress.set(1.0), 100);
|
||||
})
|
||||
|
||||
progress.subscribe((v) => {
|
||||
if (tr === undefined || !tr.style)
|
||||
return
|
||||
|
||||
tr.style.height = v*percentages.TR*200 + "px";
|
||||
nc.style.height = v*percentages.NC*200 + "px";
|
||||
vs.style.height = v*percentages.VS*200 + "px";
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -21,15 +21,26 @@
|
|||
</script>
|
||||
|
||||
<PaginatedList {fetch} let:data={logins} let:pagination={pagination}>
|
||||
<p slot="header">
|
||||
{#if pagination.item_count}
|
||||
Login data
|
||||
{:else}
|
||||
No logins yet
|
||||
{/if}
|
||||
</p>
|
||||
<table slot="body" class="table table-dark table-responsive">
|
||||
<thead>
|
||||
<td>Login Time</td>
|
||||
<td>From</td>
|
||||
<td>Login Date</td>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{#each logins as login, i}
|
||||
<tr>
|
||||
<td>{moment(login.login_time).fromNow()}</td>
|
||||
<td>
|
||||
<code>{login.hostname} - {login.ip_address}</code>
|
||||
</td>
|
||||
<td>{moment(login.login_time).format('MMMM Do YYYY, h:mm:ss a')} ({moment(login.login_time).fromNow()})</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@
|
|||
import { get_initial_state, logout, isAdmin, loggedIn, username } from '../UserState.js';
|
||||
import axios from 'axios'
|
||||
export let route;
|
||||
export let pageCtx;
|
||||
console.log(pageCtx)
|
||||
</script>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark justify-content-between">
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import axios from 'axios'
|
||||
import Pagination from '../components/Pagination'
|
||||
|
||||
export let setURLParam = false;
|
||||
export let fetch;
|
||||
|
||||
let data;
|
||||
|
|
@ -15,12 +16,16 @@
|
|||
|
||||
onMount(async () => {
|
||||
const url = new URL(window.location.href)
|
||||
let page = url.searchParams.get('page')
|
||||
let initialPage = 1;
|
||||
|
||||
if (page == undefined)
|
||||
page = 1;
|
||||
if (setURLParam) {
|
||||
let param = parseInt(url.searchParams.get('page'))
|
||||
|
||||
await list_fetch(page);
|
||||
if (param != NaN)
|
||||
initialPage = param;
|
||||
}
|
||||
|
||||
await list_fetch(initialPage);
|
||||
})
|
||||
|
||||
async function pageChange(page) {
|
||||
|
|
@ -50,8 +55,10 @@
|
|||
|
||||
{#if data}
|
||||
<slot name="header" data={data} pagination={pagination}></slot>
|
||||
<Pagination {pagination} {pageChange} />
|
||||
{#if pagination.item_count > 0}
|
||||
<Pagination {pagination} {pageChange} {setURLParam} />
|
||||
<slot name="body" data={data} pagination={pagination}></slot>
|
||||
<Pagination {pagination} {pageChange} />
|
||||
<Pagination {pagination} {pageChange} {setURLParam} />
|
||||
{/if}
|
||||
<slot name="footer" data={data} pagination={pagination}></slot>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,31 @@
|
|||
<script>
|
||||
export let pagination;
|
||||
export let pageChange;
|
||||
let numPages = 10;
|
||||
export let setURLParam = false;
|
||||
export let displayPages = 10;
|
||||
|
||||
let pages = []
|
||||
|
||||
function pageClick(event) {
|
||||
const page = event.target.getAttribute('data-page');
|
||||
pageChange(parseInt(page))
|
||||
|
||||
if (!setURLParam)
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
$ : {
|
||||
const new_pages = [];
|
||||
let pi = 0, i;
|
||||
let pg = pagination;
|
||||
|
||||
const pageChunk = Math.max(Math.ceil(numPages/3), 1);
|
||||
const pageChunk = Math.max(Math.ceil(displayPages/3), 1);
|
||||
const middleChunk = Math.max(Math.ceil(pageChunk/2), 1);
|
||||
const leftBound = Math.min(pageChunk+1, pagination.page_count);
|
||||
const rightBound = Math.max(pagination.page_count-pageChunk, 1);
|
||||
|
||||
// fast path: draw all pages
|
||||
if (pg.page_count <= numPages || rightBound <= leftBound) {
|
||||
if (pg.page_count <= displayPages || rightBound <= leftBound) {
|
||||
for (i = 1; i <= pg.page_count; i++)
|
||||
new_pages[pi++] = i;
|
||||
} else {
|
||||
|
|
@ -54,27 +64,31 @@
|
|||
<p>Displaying {(pagination.page-1)*pagination.items_per_page+1} — {Math.min(pagination.page*pagination.items_per_page, pagination.item_count)}</p>
|
||||
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination pagination-sm">
|
||||
<li class="page-item" class:disabled={pagination.page<=1}>
|
||||
<a class="page-link" href={"?page="+(pagination.page-1)}
|
||||
on:click={(e) => pageChange(pagination.page-1)}
|
||||
aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{#each pages as page,i}
|
||||
<ul class="pagination pagination-sm">
|
||||
<li class="page-item" class:disabled={pagination.page<=1}>
|
||||
<a class="page-link" href={"?page="+(pagination.page-1)}
|
||||
on:click={pageClick}
|
||||
data-page={pagination.page-1}
|
||||
aria-label="Previous">
|
||||
«
|
||||
</a>
|
||||
</li>
|
||||
{#each pages as page,i}
|
||||
{#if page == -1}
|
||||
<li class="page-item page-last-separator disabled" ><a class="page-link">...<a></li>
|
||||
<li class="page-item page-last-separator disabled"><span class="page-link">...</span></li>
|
||||
{:else}
|
||||
<li class="page-item" class:active={page==pagination.page}><a on:click={(e) => pageChange(page)} href={"?page="+page} class="page-link">{page}</a></li>
|
||||
<li class="page-item" class:active={page==pagination.page}>
|
||||
<a on:click={pageClick} href={"?page="+page} data-page={page} class="page-link">{page}</a>
|
||||
</li>
|
||||
{/if}
|
||||
{/each}
|
||||
<li class="page-item" class:disabled={pagination.page>=pagination.page_count}>
|
||||
<a class="page-link" href={"?page="+(pagination.page+1)}
|
||||
on:click={(e) => pageChange(pagination.page+1)}
|
||||
aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{/each}
|
||||
<li class="page-item" class:disabled={pagination.page>=pagination.page_count}>
|
||||
<a class="page-link" href={"?page="+(pagination.page+1)}
|
||||
data-page={pagination.page+1}
|
||||
on:click={pageClick}
|
||||
aria-label="Next">
|
||||
»
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
|
|||
|
|
@ -4,9 +4,15 @@
|
|||
import page from 'page'
|
||||
import moment from 'moment'
|
||||
import CharacterLink from '../components/CharacterLink'
|
||||
import { userId } from '../UserState'
|
||||
import LoginList from '../components/LoginList'
|
||||
import AccountLink from '../components/AccountLink'
|
||||
|
||||
export let pageCtx;
|
||||
export let appAlert;
|
||||
export let params;
|
||||
export let ready;
|
||||
|
||||
ready = false;
|
||||
|
||||
let username;
|
||||
|
|
@ -17,8 +23,10 @@
|
|||
let account;
|
||||
|
||||
onMount(async () => {
|
||||
let loadID = params.id || $userId;
|
||||
|
||||
try {
|
||||
const resp = await axios.get("/api/user/profile")
|
||||
const resp = await axios.get("/api/user/"+loadID+"/profile")
|
||||
account = resp.data;
|
||||
username = resp.data.name;
|
||||
characters = resp.data.characters;
|
||||
|
|
@ -30,7 +38,13 @@
|
|||
ready = true
|
||||
} catch (e) {
|
||||
if (e.response && e.response.status == 403) {
|
||||
page("/login?redirect=/profile")
|
||||
if (e.response.data.message == "session required") {
|
||||
page("/login?redirect="+pageCtx.pathname)
|
||||
} else {
|
||||
appAlert.message(e.response.data.message)
|
||||
}
|
||||
} else {
|
||||
appAlert.message(e.message)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -40,17 +54,9 @@
|
|||
<title>PSForever - Profile</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1>Your Account</h1>
|
||||
{#if account}
|
||||
<h1>Account: <AccountLink account={account}/></h1>
|
||||
<form>
|
||||
{#if isAdmin}
|
||||
<strong class="color-red">You are a GM.</strong>
|
||||
{/if}
|
||||
<div class="form-group row">
|
||||
<label for="staticUsername" class="col-sm-2 col-form-label">Username</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" readonly class="form-control-plaintext" id="staticUsername" bind:value={username}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="staticEmail" class="col-sm-2 col-form-label">Email</label>
|
||||
<div class="col-sm-10">
|
||||
|
|
@ -66,17 +72,21 @@
|
|||
</form>
|
||||
|
||||
<h2>Characters</h2>
|
||||
{#if characters.length > 1}
|
||||
<div class="row">
|
||||
{#each characters as char, i}
|
||||
<div class="col-md-4 col-12"><CharacterLink character={char} /></div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<p>You have no characters</p>
|
||||
{/if}
|
||||
<p>
|
||||
{#if characters.length > 1}
|
||||
<div class="row">
|
||||
{#each characters as char, i}
|
||||
<div class="col-md-4 col-12"><CharacterLink character={char} /></div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
You have no characters
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
<h2>Logins</h2>
|
||||
{#if account}
|
||||
<p>
|
||||
<LoginList account_id={account.id} />
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue