Added admin panel

This commit is contained in:
a-ill 2023-08-11 12:25:48 +03:00
parent 40f8274d7c
commit d634cbb65f
10 changed files with 903 additions and 22 deletions

View File

@ -0,0 +1,145 @@
module AdminController
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests, GenieAuthentication, DataFrames, GenieAuthorisation
using JSON3
using SearchLight,SearchLightPostgreSQL, LibPQ, JSON3
using Server.DatabaseSupport, Server.TemplateEditor, Server.Users
import Server.DatabaseSupport: select_from_table, insert_into_table, delete_from_table, exist_in_table
controller = "admin"
dict_layouts = Dict(
:admin_panel => generate_layout_html("main",controller,"admin-panel"),
)
#---Page info-----------------------------------------------------
const admin_panel_info = Dict(
"en" => Dict(
:title => "LibSoc - Admin panel",
:description => ""
),
"ru" => Dict(
:title => "",
:description => ""
)
)
function get_locale()
data = payload()
if :locale in keys(data)
return data[:locale]
else
return "en"
end
end
#---Helpers-----------------------------------------------------------
function table_to_json(name,df)
ar = []
for df_row in eachrow(df)
dict = Dict()
for id in names(df_row)
dict[id] = df_row[id]
end
push!(ar,dict)
end
open("public/assets/"*name*".json", "w") do io
JSON3.write(io, ar)
end
end
function compile(name)
df = select_from_table([name => ["*"]])
table_to_json(name,df)
end
function move_requests(name)
df_requests = select_from_table(["$(name)_requests" => ["*"]], where_data=["verified" => true, "added" => false])
df = select_from_table([name => ["*"]])
latitudes = df.latitude
longitudes = df.longitude
for df_row in eachrow(df_requests)
ind_id_given = ismissing(df_row.id_given) ? nothing : findfirst(df_row.id_given.==df.id)
if (!isnothing(ind_id_given))
id = df[ind_id_given,:id]
row_found = df[ind_id_given,Not(:id)]
dict = Dict(zip(names(row_found),values(row_found)))
dict["members"] += 1
update_table(name,dict, where_data=["id" => id])
else
id = df_row.id
dict_update = Dict("added" => true)
update_table("$(name)_requests",dict_update, where_data=["id" => id])
df_row_to_add = df_row[Not(:id_given)]
df_row_to_add = df_row_to_add[Not(:verified)]
df_row_to_add = df_row_to_add[Not(:added)]
df_row_to_add = df_row_to_add[Not(:id)]
dict = Dict(zip(names(df_row_to_add),values(df_row_to_add)))
dict["members"] = 1
insert_into_table(name,dict)
end
end
end
#---Functions---------------------------------------------------------
current_user() = findone(Users.User, id = get_authentication())
function admin_panel()
@info has_permission(current_user(), "verification")
@info current_user()
if has_permission(current_user(), "verification")
locale = get_locale()
html(:admin,:admin_panel, layout = dict_layouts[:admin_panel], context = @__MODULE__,
title = admin_panel_info[locale][:title],
description = admin_panel_info[locale][:description]
)
end
end
function verify()
if has_permission(current_user(), "verification")
data = copy(jsonpayload())
user_id = data["user_id"]
update_table("users",Dict("verified" => true), where_data=["id" => user_id])
return nothing
end
end
function get_unverified_users()
if has_permission(current_user(), "verification")
users = select_from_table("users" => ["id","email"], where_data = ["verified" => false])
data = []
if size(users,1)!=0
for x in eachrow(users)
dict = Dict("user_id" => x["id"],"email" => x["email"])
push!(data, dict)
end
end
return JSON3.write(data)
end
end
function add_verified_groups()
if has_permission(current_user(), "admin")
groups_create_requests_verified = select_from_table("groups_requests" => ["*"], where_data = ["group_id" => nothing, "status" => 1])
if size(groups_create_requests_verified,1)!=0
data = Dict(zip(names(groups_create_requests_verified),groups_create_requests_verified[end,:]))
user_id = data["user_id"]
delete!(data,"group_id")
delete!(data,"user_id")
delete!(data,"id")
delete!(data,"status")
group_id = insert_into_table("groups",data, "RETURNING id")[1,1]
dict_users_groups = Dict("user_id" => user_id, "group_id" => group_id)
insert_into_table("users_groups",dict_users_groups)
delete_from_table("groups_requests",["user_id" => user_id])
end
compile("groups")
end
end
end

View File

@ -0,0 +1 @@
<admin-panel></admin-panel>

View File

@ -245,23 +245,6 @@ function reject_request()
return nothing return nothing
end end
function add_verified_groups()
groups_create_requests_verified = select_from_table("groups_requests" => ["*"], where_data = ["group_id" => nothing, "status" => 1])
if size(groups_create_requests_verified,1)!=0
data = Dict(zip(names(groups_create_requests_verified),groups_create_requests_verified[end,:]))
user_id = data["user_id"]
delete!(data,"group_id")
delete!(data,"user_id")
delete!(data,"id")
delete!(data,"status")
group_id = insert_into_table("groups",data, "RETURNING id")[1,1]
dict_users_groups = Dict("user_id" => user_id, "group_id" => group_id)
insert_into_table("users_groups",dict_users_groups)
delete_from_table("groups_requests",["user_id" => user_id])
end
compile("groups")
end
function changeMemberCount() function changeMemberCount()
user_id = get_authentication() user_id = get_authentication()
groups_ids = select_from_table("users_groups" => ["group_id"], where_data = ["user_id" => user_id])[:,1] groups_ids = select_from_table("users_groups" => ["group_id"], where_data = ["user_id" => user_id])[:,1]

View File

@ -0,0 +1,140 @@
<svelte:options tag="admin-panel" />
<script>
// Import statements
import { onMount, getContext } from 'svelte'
import { writable } from 'svelte/store'
import { getData, sendData } from "/js/libraries/serverTools.js"
//Import components
import "/js/components/select-component.js"
import "/js/components/switch-component.js"
import "/js/components/pane-aligner.js"
//Export statements
// Main code
let section
let requests_verification = []
let loaded = writable(0)
let keyRequests = 0
let numLoaded = 1
let mainPane
let context = getContext("profile-component")
function requests_callback(response) {
let parsed = JSON.parse(response)
requests_verification.push(...parsed)
loaded.update((val) => {
return val + 1
})
}
getData("/xx/get-unverified-users",requests_callback)
function approveRequest(ind,user_id) {
sendData("/xx/verify",{user_id: user_id})
requests_verification.splice(ind,1)
keyRequests = keyRequests + 1
}
function addVerified() {
getData("/xx/add-verified-groups",() => "")
}
onMount(() => {
})
</script>
{#key $loaded}
{#if $loaded==numLoaded}
<pane-aligner>
<div bind:this={mainPane} slot="main">
<h3>User verification</h3>
<section bind:this={section} class="entries-section">
{#key keyRequests}
{#each requests_verification as req,ind}
<div>
<div class="change-field-line">
<span>{req.email}</span>
<div class="request-button-wrapper">
<button on:click={() => approveRequest(ind,req.user_id)} class="default-button approve-button">Approve</button>
</div>
</div>
</div>
{/each}
{/key}
<button on:click={addVerified} id="add-verified-button" class="default-button">Add verified pins</button>
</section>
</div>
</pane-aligner>
{/if}
{/key}
<style>
@import '/css/common.css';
.request-button-wrapper {
display: flex;
gap: 1rem;
}
.default-button {
height: 2.7rem;
padding: 0rem 1rem;
font-family: var(--sans-serif,sans-serif);
font-size: 1.15rem;
color: white;
background-color: var(--red);
border-color: var(--red);
border-radius: 0.5rem;
}
.approve-button {
margin-top: -0.45rem;
}
#add-verified-button {
margin-top: 1rem;
}
.entries-section {
margin-bottom: 1rem;
}
.entries-section >div {
height: 3.5rem;
padding-bottom: 0.75rem;
padding-top: 0.75rem;
border-bottom: 0.14rem solid;
border-color: #cdcdcd;
}
/* add padding to every line to center the diving line*/
.entries-section >div:last-child {
padding-bottom: 0.75rem;
padding-top: 0.75rem;
border-bottom: 0;
}
/*---Change field-------------------------------------------------------------------*/
.change-field-line {
display: flex;
justify-content: space-between;
}
/*---General section-----------------------------------------------------------*/
h3 {
margin-bottom: 0.5rem;
}
span {
font-family: var(--sans-serif,sans-serif);
font-size: 1.15rem;
}
</style>

View File

@ -25,6 +25,9 @@ SearchLight.Configuration.load() |> SearchLight.connect
#SearchLight.Migration.all_up!!(context=Server) #SearchLight.Migration.all_up!!(context=Server)
#SearchLight.Migration.status() #SearchLight.Migration.status()
#---Create tables----------------------------------------------------
p = "db/migrations/" p = "db/migrations/"
files = readdir(p) files = readdir(p)
files = files[map(x -> x[end-1:end].=="jl", files)] files = files[map(x -> x[end-1:end].=="jl", files)]
@ -39,3 +42,23 @@ for f in files
catch catch
end end
end end
#---Initialize Genie Authorization----------------------------------------------------
using GenieAuthorisation
using GenieAuthorisation: findone_or_create, save!, findone
# Create roles
for r in ["admin"]
findone_or_create(Role, name = r) |> save!
end
# Create permissions
for p in ["verification"]
findone_or_create(Permission, name = p) |> save!
end
assign_permission(findone(Role, name = "admin"), findone(Permission, name = "verification"))
# assign_role(findone(User, email = "user@user"), findone(Role, name = "admin"))

View File

@ -0,0 +1 @@
import{S as t,i as e,a as n,b as o,s,e as r,n as i,d as a,c,g as d,o as l,f as m,h as p,j as u,k as f,l as h,m as g,p as b,t as v}from"./index-0d9f0c09.js";import{w as x}from"./index-1c123138.js";import{getData as j,sendData as w}from"../../../../../../../../../js/libraries/serverTools.js";import"../../../../../../../../../js/components/select-component.js";import"../../../../../../../../../js/components/switch-component.js";import"../../../../../../../../../js/components/pane-aligner.js";function y(t,e,n){const o=t.slice();return o[13]=e[n],o[15]=n,o}function k(t){let e,n,r,i,c,d,l,m,b,v=t[1],x=C(t);return{c(){e=p("pane-aligner"),n=p("div"),r=p("h3"),r.textContent="User verification",i=u(),c=p("section"),x.c(),d=u(),l=p("button"),l.textContent="Add verified pins",f(l,"id","add-verified-button"),f(l,"class","default-button"),f(c,"class","entries-section"),f(n,"slot","main")},m(s,a){o(s,e,a),h(e,n),h(n,r),h(n,i),h(n,c),x.m(c,null),h(c,d),h(c,l),t[9](c),t[10](n),m||(b=g(l,"click",t[7]),m=!0)},p(t,e){2&e&&s(v,v=t[1])?(x.d(1),x=C(t),x.c(),x.m(c,d)):x.p(t,e)},d(n){n&&a(e),x.d(n),t[9](null),t[10](null),m=!1,b()}}}function N(t){let e,n,s,r,i,c,d,l,m,b,x=t[13].email+"";function j(){return t[8](t[15],t[13])}return{c(){e=p("div"),n=p("div"),s=p("span"),r=v(x),i=u(),c=p("div"),d=p("button"),d.textContent="Approve",l=u(),f(d,"class","default-button approve-button"),f(c,"class","request-button-wrapper"),f(n,"class","change-field-line")},m(t,a){o(t,e,a),h(e,n),h(n,s),h(s,r),h(n,i),h(n,c),h(c,d),h(e,l),m||(b=g(d,"click",j),m=!0)},p(e,n){t=e},d(t){t&&a(e),m=!1,b()}}}function C(t){let e,n=t[4],s=[];for(let e=0;e<n.length;e+=1)s[e]=N(y(t,n,e));return{c(){for(let t=0;t<s.length;t+=1)s[t].c();e=r()},m(t,n){for(let e=0;e<s.length;e+=1)s[e].m(t,n);o(t,e,n)},p(t,o){if(80&o){let r;for(n=t[4],r=0;r<n.length;r+=1){const i=y(t,n,r);s[r]?s[r].p(i,o):(s[r]=N(i),s[r].c(),s[r].m(e.parentNode,e))}for(;r<s.length;r+=1)s[r].d(1);s.length=n.length}},d(t){b(s,t),t&&a(e)}}}function q(t){let e,n=t[3]==A&&k(t);return{c(){n&&n.c(),e=r()},m(t,s){n&&n.m(t,s),o(t,e,s)},p(t,o){t[3]==A?n?n.p(t,o):(n=k(t),n.c(),n.m(e.parentNode,e)):n&&(n.d(1),n=null)},d(t){n&&n.d(t),t&&a(e)}}}function z(t){let e,n=t[3],c=q(t);return{c(){c.c(),e=r(),this.c=i},m(t,n){c.m(t,n),o(t,e,n)},p(t,[o]){8&o&&s(n,n=t[3])?(c.d(1),c=q(t),c.c(),c.m(e.parentNode,e)):c.p(t,o)},i:i,o:i,d(t){t&&a(e),c.d(t)}}}let A=1;function E(t,e,n){let o,s,r=[],i=x(0);c(t,i,(t=>n(3,o=t)));let a,p=0;function u(t,e){w("/xx/verify",{user_id:e}),r.splice(t,1),n(1,p+=1)}d("profile-component"),j("/xx/get-unverified-users",(function(t){let e=JSON.parse(t);r.push(...e),i.update((t=>t+1))})),l((()=>{}));return[s,p,a,o,r,i,u,function(){j("/xx/add-verified-groups",(()=>""))},(t,e)=>u(t,e.user_id),function(t){m[t?"unshift":"push"]((()=>{s=t,n(0,s)}))},function(t){m[t?"unshift":"push"]((()=>{a=t,n(2,a)}))}]}class R extends t{constructor(t){super(),this.shadowRoot.innerHTML="<style>@import '/css/common.css';.request-button-wrapper{display:flex;gap:1rem}.default-button{height:2.7rem;padding:0rem 1rem;font-family:var(--sans-serif,sans-serif);font-size:1.15rem;color:white;background-color:var(--red);border-color:var(--red);border-radius:0.5rem}.approve-button{margin-top:-0.45rem}#add-verified-button{margin-top:1rem}.entries-section{margin-bottom:1rem}.entries-section>div{height:3.5rem;padding-bottom:0.75rem;padding-top:0.75rem;border-bottom:0.14rem solid;border-color:#cdcdcd}.entries-section>div:last-child{padding-bottom:0.75rem;padding-top:0.75rem;border-bottom:0}.change-field-line{display:flex;justify-content:space-between}h3{margin-bottom:0.5rem}span{font-family:var(--sans-serif,sans-serif);font-size:1.15rem}</style>",e(this,{target:this.shadowRoot,props:n(this.attributes),customElement:!0},E,z,s,{},null),t&&t.target&&o(t.target,this,t.anchor)}}customElements.define("admin-panel",R);export{R as default};

View File

@ -0,0 +1,527 @@
(function(l, r) { if (!l || l.getElementById('livereloadscript')) return; r = l.createElement('script'); r.async = 1; r.src = '//' + (self.location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; r.id = 'livereloadscript'; l.getElementsByTagName('head')[0].appendChild(r) })(self.document);
function noop() { }
function add_location(element, file, line, column, char) {
element.__svelte_meta = {
loc: { file, line, column, char }
};
}
function run(fn) {
return fn();
}
function blank_object() {
return Object.create(null);
}
function run_all(fns) {
fns.forEach(run);
}
function is_function(thing) {
return typeof thing === 'function';
}
function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
}
let src_url_equal_anchor;
function src_url_equal(element_src, url) {
if (!src_url_equal_anchor) {
src_url_equal_anchor = document.createElement('a');
}
src_url_equal_anchor.href = url;
return element_src === src_url_equal_anchor.href;
}
function is_empty(obj) {
return Object.keys(obj).length === 0;
}
function validate_store(store, name) {
if (store != null && typeof store.subscribe !== 'function') {
throw new Error(`'${name}' is not a store with a 'subscribe' method`);
}
}
function subscribe(store, ...callbacks) {
if (store == null) {
return noop;
}
const unsub = store.subscribe(...callbacks);
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
}
function component_subscribe(component, store, callback) {
component.$$.on_destroy.push(subscribe(store, callback));
}
function append(target, node) {
target.appendChild(node);
}
function insert(target, node, anchor) {
target.insertBefore(node, anchor || null);
}
function detach(node) {
node.parentNode.removeChild(node);
}
function destroy_each(iterations, detaching) {
for (let i = 0; i < iterations.length; i += 1) {
if (iterations[i])
iterations[i].d(detaching);
}
}
function element(name) {
return document.createElement(name);
}
function svg_element(name) {
return document.createElementNS('http://www.w3.org/2000/svg', name);
}
function text(data) {
return document.createTextNode(data);
}
function space() {
return text(' ');
}
function empty() {
return text('');
}
function listen(node, event, handler, options) {
node.addEventListener(event, handler, options);
return () => node.removeEventListener(event, handler, options);
}
function attr(node, attribute, value) {
if (value == null)
node.removeAttribute(attribute);
else if (node.getAttribute(attribute) !== value)
node.setAttribute(attribute, value);
}
function set_custom_element_data(node, prop, value) {
if (prop in node) {
node[prop] = typeof node[prop] === 'boolean' && value === '' ? true : value;
}
else {
attr(node, prop, value);
}
}
function to_number(value) {
return value === '' ? null : +value;
}
function children(element) {
return Array.from(element.childNodes);
}
function set_input_value(input, value) {
input.value = value == null ? '' : value;
}
function set_style(node, key, value, important) {
if (value === null) {
node.style.removeProperty(key);
}
else {
node.style.setProperty(key, value, important ? 'important' : '');
}
}
function custom_event(type, detail, { bubbles = false, cancelable = false } = {}) {
const e = document.createEvent('CustomEvent');
e.initCustomEvent(type, bubbles, cancelable, detail);
return e;
}
class HtmlTag {
constructor(is_svg = false) {
this.is_svg = false;
this.is_svg = is_svg;
this.e = this.n = null;
}
c(html) {
this.h(html);
}
m(html, target, anchor = null) {
if (!this.e) {
if (this.is_svg)
this.e = svg_element(target.nodeName);
else
this.e = element(target.nodeName);
this.t = target;
this.c(html);
}
this.i(anchor);
}
h(html) {
this.e.innerHTML = html;
this.n = Array.from(this.e.childNodes);
}
i(anchor) {
for (let i = 0; i < this.n.length; i += 1) {
insert(this.t, this.n[i], anchor);
}
}
p(html) {
this.d();
this.h(html);
this.i(this.a);
}
d() {
this.n.forEach(detach);
}
}
function attribute_to_object(attributes) {
const result = {};
for (const attribute of attributes) {
result[attribute.name] = attribute.value;
}
return result;
}
let current_component;
function set_current_component(component) {
current_component = component;
}
function get_current_component() {
if (!current_component)
throw new Error('Function called outside component initialization');
return current_component;
}
/**
* The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM.
* It must be called during the component's initialisation (but doesn't need to live *inside* the component;
* it can be called from an external module).
*
* `onMount` does not run inside a [server-side component](/docs#run-time-server-side-component-api).
*
* https://svelte.dev/docs#run-time-svelte-onmount
*/
function onMount(fn) {
get_current_component().$$.on_mount.push(fn);
}
/**
* Schedules a callback to run immediately after the component has been updated.
*
* The first time the callback runs will be after the initial `onMount`
*/
function afterUpdate(fn) {
get_current_component().$$.after_update.push(fn);
}
/**
* Associates an arbitrary `context` object with the current component and the specified `key`
* and returns that object. The context is then available to children of the component
* (including slotted content) with `getContext`.
*
* Like lifecycle functions, this must be called during component initialisation.
*
* https://svelte.dev/docs#run-time-svelte-setcontext
*/
function setContext(key, context) {
get_current_component().$$.context.set(key, context);
return context;
}
/**
* Retrieves the context that belongs to the closest parent component with the specified `key`.
* Must be called during component initialisation.
*
* https://svelte.dev/docs#run-time-svelte-getcontext
*/
function getContext(key) {
return get_current_component().$$.context.get(key);
}
const dirty_components = [];
const binding_callbacks = [];
const render_callbacks = [];
const flush_callbacks = [];
const resolved_promise = Promise.resolve();
let update_scheduled = false;
function schedule_update() {
if (!update_scheduled) {
update_scheduled = true;
resolved_promise.then(flush);
}
}
function add_render_callback(fn) {
render_callbacks.push(fn);
}
// flush() calls callbacks in this order:
// 1. All beforeUpdate callbacks, in order: parents before children
// 2. All bind:this callbacks, in reverse order: children before parents.
// 3. All afterUpdate callbacks, in order: parents before children. EXCEPT
// for afterUpdates called during the initial onMount, which are called in
// reverse order: children before parents.
// Since callbacks might update component values, which could trigger another
// call to flush(), the following steps guard against this:
// 1. During beforeUpdate, any updated components will be added to the
// dirty_components array and will cause a reentrant call to flush(). Because
// the flush index is kept outside the function, the reentrant call will pick
// up where the earlier call left off and go through all dirty components. The
// current_component value is saved and restored so that the reentrant call will
// not interfere with the "parent" flush() call.
// 2. bind:this callbacks cannot trigger new flush() calls.
// 3. During afterUpdate, any updated components will NOT have their afterUpdate
// callback called a second time; the seen_callbacks set, outside the flush()
// function, guarantees this behavior.
const seen_callbacks = new Set();
let flushidx = 0; // Do *not* move this inside the flush() function
function flush() {
const saved_component = current_component;
do {
// first, call beforeUpdate functions
// and update components
while (flushidx < dirty_components.length) {
const component = dirty_components[flushidx];
flushidx++;
set_current_component(component);
update(component.$$);
}
set_current_component(null);
dirty_components.length = 0;
flushidx = 0;
while (binding_callbacks.length)
binding_callbacks.pop()();
// then, once components are updated, call
// afterUpdate functions. This may cause
// subsequent updates...
for (let i = 0; i < render_callbacks.length; i += 1) {
const callback = render_callbacks[i];
if (!seen_callbacks.has(callback)) {
// ...so guard against infinite loops
seen_callbacks.add(callback);
callback();
}
}
render_callbacks.length = 0;
} while (dirty_components.length);
while (flush_callbacks.length) {
flush_callbacks.pop()();
}
update_scheduled = false;
seen_callbacks.clear();
set_current_component(saved_component);
}
function update($$) {
if ($$.fragment !== null) {
$$.update();
run_all($$.before_update);
const dirty = $$.dirty;
$$.dirty = [-1];
$$.fragment && $$.fragment.p($$.ctx, dirty);
$$.after_update.forEach(add_render_callback);
}
}
const outroing = new Set();
function transition_in(block, local) {
if (block && block.i) {
outroing.delete(block);
block.i(local);
}
}
const globals = (typeof window !== 'undefined'
? window
: typeof globalThis !== 'undefined'
? globalThis
: global);
function mount_component(component, target, anchor, customElement) {
const { fragment, after_update } = component.$$;
fragment && fragment.m(target, anchor);
if (!customElement) {
// onMount happens before the initial afterUpdate
add_render_callback(() => {
const new_on_destroy = component.$$.on_mount.map(run).filter(is_function);
// if the component was destroyed immediately
// it will update the `$$.on_destroy` reference to `null`.
// the destructured on_destroy may still reference to the old array
if (component.$$.on_destroy) {
component.$$.on_destroy.push(...new_on_destroy);
}
else {
// Edge case - component was destroyed immediately,
// most likely as a result of a binding initialising
run_all(new_on_destroy);
}
component.$$.on_mount = [];
});
}
after_update.forEach(add_render_callback);
}
function destroy_component(component, detaching) {
const $$ = component.$$;
if ($$.fragment !== null) {
run_all($$.on_destroy);
$$.fragment && $$.fragment.d(detaching);
// TODO null out other refs, including component.$$ (but need to
// preserve final state?)
$$.on_destroy = $$.fragment = null;
$$.ctx = [];
}
}
function make_dirty(component, i) {
if (component.$$.dirty[0] === -1) {
dirty_components.push(component);
schedule_update();
component.$$.dirty.fill(0);
}
component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));
}
function init(component, options, instance, create_fragment, not_equal, props, append_styles, dirty = [-1]) {
const parent_component = current_component;
set_current_component(component);
const $$ = component.$$ = {
fragment: null,
ctx: [],
// state
props,
update: noop,
not_equal,
bound: blank_object(),
// lifecycle
on_mount: [],
on_destroy: [],
on_disconnect: [],
before_update: [],
after_update: [],
context: new Map(options.context || (parent_component ? parent_component.$$.context : [])),
// everything else
callbacks: blank_object(),
dirty,
skip_bound: false,
root: options.target || parent_component.$$.root
};
append_styles && append_styles($$.root);
let ready = false;
$$.ctx = instance
? instance(component, options.props || {}, (i, ret, ...rest) => {
const value = rest.length ? rest[0] : ret;
if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
if (!$$.skip_bound && $$.bound[i])
$$.bound[i](value);
if (ready)
make_dirty(component, i);
}
return ret;
})
: [];
$$.update();
ready = true;
run_all($$.before_update);
// `false` as a special case of no DOM component
$$.fragment = create_fragment ? create_fragment($$.ctx) : false;
if (options.target) {
if (options.hydrate) {
const nodes = children(options.target);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.l(nodes);
nodes.forEach(detach);
}
else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.c();
}
if (options.intro)
transition_in(component.$$.fragment);
mount_component(component, options.target, options.anchor, options.customElement);
flush();
}
set_current_component(parent_component);
}
let SvelteElement;
if (typeof HTMLElement === 'function') {
SvelteElement = class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
const { on_mount } = this.$$;
this.$$.on_disconnect = on_mount.map(run).filter(is_function);
// @ts-ignore todo: improve typings
for (const key in this.$$.slotted) {
// @ts-ignore todo: improve typings
this.appendChild(this.$$.slotted[key]);
}
}
attributeChangedCallback(attr, _oldValue, newValue) {
this[attr] = newValue;
}
disconnectedCallback() {
run_all(this.$$.on_disconnect);
}
$destroy() {
destroy_component(this, 1);
this.$destroy = noop;
}
$on(type, callback) {
// TODO should this delegate to addEventListener?
if (!is_function(callback)) {
return noop;
}
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
callbacks.push(callback);
return () => {
const index = callbacks.indexOf(callback);
if (index !== -1)
callbacks.splice(index, 1);
};
}
$set($$props) {
if (this.$$set && !is_empty($$props)) {
this.$$.skip_bound = true;
this.$$set($$props);
this.$$.skip_bound = false;
}
}
};
}
function dispatch_dev(type, detail) {
document.dispatchEvent(custom_event(type, Object.assign({ version: '3.52.0' }, detail), { bubbles: true }));
}
function append_dev(target, node) {
dispatch_dev('SvelteDOMInsert', { target, node });
append(target, node);
}
function insert_dev(target, node, anchor) {
dispatch_dev('SvelteDOMInsert', { target, node, anchor });
insert(target, node, anchor);
}
function detach_dev(node) {
dispatch_dev('SvelteDOMRemove', { node });
detach(node);
}
function listen_dev(node, event, handler, options, has_prevent_default, has_stop_propagation) {
const modifiers = options === true ? ['capture'] : options ? Array.from(Object.keys(options)) : [];
if (has_prevent_default)
modifiers.push('preventDefault');
if (has_stop_propagation)
modifiers.push('stopPropagation');
dispatch_dev('SvelteDOMAddEventListener', { node, event, handler, modifiers });
const dispose = listen(node, event, handler, options);
return () => {
dispatch_dev('SvelteDOMRemoveEventListener', { node, event, handler, modifiers });
dispose();
};
}
function attr_dev(node, attribute, value) {
attr(node, attribute, value);
if (value == null)
dispatch_dev('SvelteDOMRemoveAttribute', { node, attribute });
else
dispatch_dev('SvelteDOMSetAttribute', { node, attribute, value });
}
function prop_dev(node, property, value) {
node[property] = value;
dispatch_dev('SvelteDOMSetProperty', { node, property, value });
}
function set_data_dev(text, data) {
data = '' + data;
if (text.wholeText === data)
return;
dispatch_dev('SvelteDOMSetData', { node: text, data });
text.data = data;
}
function validate_each_argument(arg) {
if (typeof arg !== 'string' && !(arg && typeof arg === 'object' && 'length' in arg)) {
let msg = '{#each} only iterates over array-like objects.';
if (typeof Symbol === 'function' && arg && Symbol.iterator in arg) {
msg += ' You can use a spread to convert this iterable into an array.';
}
throw new Error(msg);
}
}
function validate_slots(name, slot, keys) {
for (const slot_key of Object.keys(slot)) {
if (!~keys.indexOf(slot_key)) {
console.warn(`<${name}> received an unexpected slot "${slot_key}".`);
}
}
}
export { run_all as A, flush as B, is_function as C, src_url_equal as D, set_custom_element_data as E, prop_dev as F, set_style as G, svg_element as H, HtmlTag as I, afterUpdate as J, to_number as K, set_input_value as L, SvelteElement as S, attribute_to_object as a, insert_dev as b, validate_store as c, dispatch_dev as d, component_subscribe as e, globals as f, getContext as g, empty as h, init as i, detach_dev as j, binding_callbacks as k, validate_each_argument as l, space as m, noop as n, onMount as o, set_data_dev as p, element as q, add_location as r, safe_not_equal as s, text as t, attr_dev as u, validate_slots as v, append_dev as w, listen_dev as x, destroy_each as y, setContext as z };

View File

@ -0,0 +1,53 @@
(function(l, r) { if (!l || l.getElementById('livereloadscript')) return; r = l.createElement('script'); r.async = 1; r.src = '//' + (self.location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; r.id = 'livereloadscript'; l.getElementsByTagName('head')[0].appendChild(r) })(self.document);
import { n as noop, s as safe_not_equal } from './index-2620635a.js';
const subscriber_queue = [];
/**
* Create a `Writable` store that allows both updating and reading by subscription.
* @param {*=}value initial value
* @param {StartStopNotifier=}start start and stop notifications for subscriptions
*/
function writable(value, start = noop) {
let stop;
const subscribers = new Set();
function set(new_value) {
if (safe_not_equal(value, new_value)) {
value = new_value;
if (stop) { // store is ready
const run_queue = !subscriber_queue.length;
for (const subscriber of subscribers) {
subscriber[1]();
subscriber_queue.push(subscriber, value);
}
if (run_queue) {
for (let i = 0; i < subscriber_queue.length; i += 2) {
subscriber_queue[i][0](subscriber_queue[i + 1]);
}
subscriber_queue.length = 0;
}
}
}
}
function update(fn) {
set(fn(value));
}
function subscribe(run, invalidate = noop) {
const subscriber = [run, invalidate];
subscribers.add(subscriber);
if (subscribers.size === 1) {
stop = start(set) || noop;
}
run(value);
return () => {
subscribers.delete(subscriber);
if (subscribers.size === 0) {
stop();
stop = null;
}
};
}
return { set, update, subscribe };
}
export { writable as w };

View File

@ -1,6 +1,6 @@
using Genie.Router, Genie.Requests, Genie.Renderer.Json, JSON3, GenieAuthentication using Genie.Router, Genie.Requests, Genie.Renderer.Json, JSON3, GenieAuthentication, GenieAuthorisation
using Server.GroupsController using Server.GroupsController, Server.AdminController
#---Basic----------------------------------------------------------- #---Basic-----------------------------------------------------------
@ -12,6 +12,16 @@ route("/:locale/join-us/*", BasicController.join_us, named = :join_us)
route("/:locale/political-compass/*", BasicController.political_compass, named = :political_compass) route("/:locale/political-compass/*", BasicController.political_compass, named = :political_compass)
#---Admin panel------------------------------------------------------
route("/:locale/bread/*", AdminController.admin_panel, named = :admin_panel)
route("/:locale/get-unverified-users/*", AdminController.get_unverified_users, named = :get_unverified_users)
route("/:locale/verify/*", AdminController.verify, method = POST, named=:verify)
route("/:locale/add-verified-groups/*", AdminController.add_verified_groups, named = :add_verified_groups)
#---Authentication and such------------------------------------------ #---Authentication and such------------------------------------------
route("/:locale/auth/*", AuthenticationController.auth, named = :auth) route("/:locale/auth/*", AuthenticationController.auth, named = :auth)
@ -54,8 +64,6 @@ route("/:locale/group-reject-request/*", GroupsController.reject_request, method
route("/:locale/group-change/*", GroupsController.change_group, method = POST, named = :group_change) route("/:locale/group-change/*", GroupsController.change_group, method = POST, named = :group_change)
route("/:locale/add-verified-groups/*", GroupsController.add_verified_groups, named = :add_verified_groups)
#---Coops---------------------------------------------------------- #---Coops----------------------------------------------------------
route("/:locale/cooperatives/*", CooperativesController.cooperatives, named = :cooperatives) route("/:locale/cooperatives/*", CooperativesController.cooperatives, named = :cooperatives)