Added admin panel
This commit is contained in:
parent
40f8274d7c
commit
d634cbb65f
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
<admin-panel></admin-panel>
|
|
@ -245,23 +245,6 @@ function reject_request()
|
|||
return nothing
|
||||
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()
|
||||
user_id = get_authentication()
|
||||
groups_ids = select_from_table("users_groups" => ["group_id"], where_data = ["user_id" => user_id])[:,1]
|
||||
|
|
|
@ -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>
|
|
@ -25,6 +25,9 @@ SearchLight.Configuration.load() |> SearchLight.connect
|
|||
#SearchLight.Migration.all_up!!(context=Server)
|
||||
#SearchLight.Migration.status()
|
||||
|
||||
|
||||
#---Create tables----------------------------------------------------
|
||||
|
||||
p = "db/migrations/"
|
||||
files = readdir(p)
|
||||
files = files[map(x -> x[end-1:end].=="jl", files)]
|
||||
|
@ -38,4 +41,24 @@ for f in files
|
|||
m.up()
|
||||
catch
|
||||
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"))
|
||||
|
|
|
@ -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};
|
|
@ -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 };
|
|
@ -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 };
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
using Genie.Router, Genie.Requests, Genie.Renderer.Json, JSON3, GenieAuthentication
|
||||
using Server.GroupsController
|
||||
using Genie.Router, Genie.Requests, Genie.Renderer.Json, JSON3, GenieAuthentication, GenieAuthorisation
|
||||
using Server.GroupsController, Server.AdminController
|
||||
|
||||
#---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)
|
||||
|
||||
#---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------------------------------------------
|
||||
|
||||
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/add-verified-groups/*", GroupsController.add_verified_groups, named = :add_verified_groups)
|
||||
|
||||
#---Coops----------------------------------------------------------
|
||||
|
||||
route("/:locale/cooperatives/*", CooperativesController.cooperatives, named = :cooperatives)
|
||||
|
|
Loading…
Reference in New Issue