Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions server/handlers/domains.handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ async function remove(req, res) {

async function removeAdmin(req, res) {
const id = req.params.id;
const links = req.query.links
const links = req.query.links;

const domain = await query.domain.find({ id });

Expand Down Expand Up @@ -116,6 +116,8 @@ async function getAdmin(req, res) {
const { limit, skip } = req.context;
const search = req.query.search;
const user = req.query.user;
const sortBy = req.query.sortBy;
const sortOrder = req.query.sortOrder;
const banned = utils.parseBooleanQuery(req.query.banned);
const owner = utils.parseBooleanQuery(req.query.owner);
const links = utils.parseBooleanQuery(req.query.links);
Expand All @@ -126,7 +128,7 @@ async function getAdmin(req, res) {
};

const [data, total] = await Promise.all([
query.domain.getAdmin(match, { limit, search, user, links, skip }),
query.domain.getAdmin(match, { limit, search, user, links, skip, sortBy, sortOrder }),
query.domain.totalAdmin(match, { search, user, links })
]);

Expand All @@ -138,6 +140,15 @@ async function getAdmin(req, res) {
total_formatted: total.toLocaleString("en-US"),
limit,
skip,
query: {
sortBy: sortBy || "created_at",
sortOrder: sortOrder || "desc",
search,
user,
banned: req.query.banned,
owner: req.query.owner,
links: req.query.links
},
table_domains: domains,
})
return;
Expand Down Expand Up @@ -210,4 +221,4 @@ module.exports = {
getAdmin,
remove,
removeAdmin,
}
}
29 changes: 24 additions & 5 deletions server/handlers/links.handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ const dnsLookup = promisify(dns.lookup);
async function get(req, res) {
const { limit, skip } = req.context;
const search = req.query.search;
const sortBy = req.query.sortBy;
const sortOrder = req.query.sortOrder;
const userId = req.user.id;

const match = {
user_id: userId
};

const [data, total] = await Promise.all([
query.link.get(match, { limit, search, skip }),
query.link.get(match, { limit, search, skip, sortBy, sortOrder }),
query.link.total(match, { search })
]);

Expand All @@ -35,6 +37,11 @@ async function get(req, res) {
total,
limit,
skip,
query: {
sortBy: sortBy || "created_at",
sortOrder: sortOrder || "desc",
search
},
links: data.map(utils.sanitize.link_html),
})
return;
Expand All @@ -53,6 +60,8 @@ async function getAdmin(req, res) {
const search = req.query.search;
const user = req.query.user;
let domain = req.query.domain;
const sortBy = req.query.sortBy;
const sortOrder = req.query.sortOrder;
const banned = utils.parseBooleanQuery(req.query.banned);
const anonymous = utils.parseBooleanQuery(req.query.anonymous);
const has_domain = utils.parseBooleanQuery(req.query.has_domain);
Expand All @@ -63,15 +72,15 @@ async function getAdmin(req, res) {
...(has_domain !== undefined && { domain_id: [has_domain ? "is not" : "is", null] }),
};

// if domain is equal to the defualt domain,
// it means admins is looking for links with the defualt domain (no custom user domain)
// if domain is equal to the default domain,
// it means admin is looking for links with the default domain (no custom user domain)
if (domain === env.DEFAULT_DOMAIN) {
domain = undefined;
match.domain_id = null;
}

const [data, total] = await Promise.all([
query.link.getAdmin(match, { limit, search, user, domain, skip }),
query.link.getAdmin(match, { limit, search, user, domain, skip, sortBy, sortOrder }),
query.link.totalAdmin(match, { search, user, domain })
]);

Expand All @@ -83,6 +92,16 @@ async function getAdmin(req, res) {
total_formatted: total.toLocaleString("en-US"),
limit,
skip,
query: {
sortBy: sortBy || "created_at",
sortOrder: sortOrder || "desc",
search,
user,
domain,
banned: req.query.banned,
anonymous: req.query.anonymous,
has_domain: req.query.has_domain
},
links,
})
return;
Expand Down Expand Up @@ -659,4 +678,4 @@ module.exports = {
redirect,
redirectProtected,
redirectCustomDomainHomepage,
}
}
21 changes: 17 additions & 4 deletions server/handlers/users.handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const utils = require("../utils");
const mail = require("../mail");
const env = require("../env");

const CustomError = utils.CustomError;

async function get(req, res) {
const domains = await query.domain.get({ user_id: req.user.id });

Expand Down Expand Up @@ -63,7 +65,7 @@ async function removeByAdmin(req, res) {

async function getAdmin(req, res) {
const { limit, skip, all } = req.context;
const { role, search } = req.query;
const { role, search, sortBy, sortOrder } = req.query;
const userId = req.user.id;
const verified = utils.parseBooleanQuery(req.query.verified);
const banned = utils.parseBooleanQuery(req.query.banned);
Expand All @@ -77,7 +79,7 @@ async function getAdmin(req, res) {
};

const [data, total] = await Promise.all([
query.user.getAdmin(match, { limit, search, domains, links, skip }),
query.user.getAdmin(match, { limit, search, domains, links, skip, sortBy, sortOrder }),
query.user.totalAdmin(match, { search, domains, links })
]);

Expand All @@ -89,6 +91,16 @@ async function getAdmin(req, res) {
total_formatted: total.toLocaleString("en-US"),
limit,
skip,
query: {
sortBy: sortBy || "created_at",
sortOrder: sortOrder || "desc",
search,
role,
verified: req.query.verified,
banned: req.query.banned,
domains: req.query.domains,
links: req.query.links
},
users,
})
return;
Expand Down Expand Up @@ -138,7 +150,8 @@ async function ban(req, res) {

// 5. wait for all tasks to finish
await Promise.all(tasks).catch((err) => {
throw new CustomError("Couldn't ban entries.");
console.error("User ban operation failed:", err);
throw new CustomError(`Couldn't ban entries: ${err.message}`);
});

// 6. send response
Expand Down Expand Up @@ -182,4 +195,4 @@ module.exports = {
getAdmin,
remove,
removeByAdmin,
}
}
18 changes: 15 additions & 3 deletions server/queries/domain.queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ async function add(params) {
}

async function update(match, update) {
// if the domains' adddress is changed,
// if the domains' address is changed,
// make sure to delete the original domains from cache
let domains = []
if (env.REDIS_ENABLED && update.address) {
Expand Down Expand Up @@ -134,11 +134,23 @@ async function getAdmin(match, params) {
.offset(params.skip)
.limit(params.limit)
.fromRaw("domains")
.orderBy("domains.id", "desc")
.groupBy(1)
.groupBy("l.links_count")
.groupBy("users.email");

// Handle sorting
const sortBy = params.sortBy || "created_at";
const sortOrder = params.sortOrder || "desc";

if (sortBy === "created_at") {
query.orderBy("domains.created_at", sortOrder);
} else if (sortBy === "links_count") {
query.orderBy("l.links_count", sortOrder);
} else {
// Default fallback
query.orderBy("domains.id", "desc");
}

if (params?.user) {
const id = parseInt(params?.user);
if (Number.isNaN(id)) {
Expand Down Expand Up @@ -228,4 +240,4 @@ module.exports = {
remove,
totalAdmin,
update,
}
}
32 changes: 28 additions & 4 deletions server/queries/link.queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,20 @@ async function get(match, params) {
.select(...selectable)
.where(normalizeMatch(match))
.offset(params.skip)
.limit(params.limit)
.orderBy("links.id", "desc");
.limit(params.limit);

// handle sorting
const sortBy = params.sortBy || "created_at";
const sortOrder = params.sortOrder || "desc";

if (sortBy === "created_at") {
query.orderBy("links.created_at", sortOrder);
} else if (sortBy === "visit_count") {
query.orderBy("links.visit_count", sortOrder);
} else {
// default fallback to id desc for any invalid sortBy
query.orderBy("links.id", "desc");
}

if (params?.search) {
query[knex.compatibleILIKE](
Expand All @@ -146,10 +158,22 @@ async function getAdmin(match, params) {
});

query
.orderBy("links.id", "desc")
.offset(params.skip)
.limit(params.limit)

// handle sorting
const sortBy = params.sortBy || "created_at";
const sortOrder = params.sortOrder || "desc";

if (sortBy === "created_at") {
query.orderBy("links.created_at", sortOrder);
} else if (sortBy === "visit_count") {
query.orderBy("links.visit_count", sortOrder);
} else {
// default fallback to id desc for any invalid sortBy
query.orderBy("links.id", "desc");
}

if (params?.user) {
const id = parseInt(params?.user);
if (Number.isNaN(id)) {
Expand Down Expand Up @@ -267,7 +291,7 @@ async function update(match, update) {
update.password = await bcrypt.hash(update.password, salt);
}

// if the links' adddress or domain is changed,
// if the links' address or domain is changed,
// make sure to delete the original links from cache
let links = []
if (env.REDIS_ENABLED && (update.address || update.domain_id)) {
Expand Down
14 changes: 13 additions & 1 deletion server/queries/user.queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,22 @@ async function getAdmin(match, params) {
.where(normalizeMatch(match))
.offset(params.skip)
.limit(params.limit)
.orderBy("users.id", "desc")
.groupBy(1)
.groupBy("l.links_count")
.groupBy("d.domains");

// handle sorting
const sortBy = params.sortBy || "created_at";
const sortOrder = params.sortOrder || "desc";

if (sortBy === "created_at") {
query.orderBy("users.created_at", sortOrder);
} else if (sortBy === "links_count") {
query.orderBy("l.links_count", sortOrder);
} else {
// Default fallback
query.orderBy("users.id", "desc");
}

if (params?.search) {
const id = parseInt(params?.search);
Expand Down
6 changes: 3 additions & 3 deletions server/queues/visit.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ const osList = ["Windows", "Mac OS", "Linux", "Android", "iOS"];

function filterInBrowser(agent) {
return function(item) {
return agent.family.toLowerCase().includes(item.toLocaleLowerCase());
return agent.family.toLowerCase().includes(item.toLowerCase());
}
}

function filterInOs(agent) {
return function(item) {
return agent.os.family.toLowerCase().includes(item.toLocaleLowerCase());
return agent.os.family.toLowerCase().includes(item.toLowerCase());
}
}

Expand Down Expand Up @@ -48,4 +48,4 @@ module.exports = function({ data }) {
);

return Promise.all(tasks);
}
}
14 changes: 11 additions & 3 deletions server/views/partials/admin/domains/thead.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
<input id="total" name="total" type="hidden" value="{{total}}" />
<input id="limit" name="limit" type="hidden" value="10" />
<input id="skip" name="skip" type="hidden" value="0" />
<input id="sortBy" name="sortBy" type="hidden" value="{{#if query.sortBy}}{{query.sortBy}}{{else}}created_at{{/if}}" />
<input id="sortOrder" name="sortOrder" type="hidden" value="{{#if query.sortOrder}}{{query.sortOrder}}{{else}}desc{{/if}}" />
<button
class="table primary"
hx-on:click='openDialog("admin-table-dialog")'
Expand All @@ -81,8 +83,14 @@
<th class="domains-id">ID</th>
<th class="domains-address">Address</th>
<th class="domains-homepage">Homepage</th>
<th class="domains-created-at">Created at</th>
<th class="domains-links-count">Total links</th>
<th class="domains-created-at sortable" onclick="sortBy('created_at')">
Created at
<span class="sort-indicator"></span>
</th>
<th class="domains-links-count sortable" onclick="sortBy('links_count')">
Total links
<span class="sort-indicator"></span>
</th>
<th class="domains-actions"></th>
</tr>
</thead>
</thead>
14 changes: 11 additions & 3 deletions server/views/partials/admin/links/thead.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,23 @@
<input id="total" name="total" type="hidden" value="{{total}}" />
<input id="limit" name="limit" type="hidden" value="10" />
<input id="skip" name="skip" type="hidden" value="0" />
<input id="sortBy" name="sortBy" type="hidden" value="{{#if query.sortBy}}{{query.sortBy}}{{else}}created_at{{/if}}" />
<input id="sortOrder" name="sortOrder" type="hidden" value="{{#if query.sortOrder}}{{query.sortOrder}}{{else}}desc{{/if}}" />
</div>
</th>
{{> admin/table_nav}}
</tr>
<tr>
<th class="original-url">Original URL</th>
<th class="created-at">Created at</th>
<th class="created-at sortable" onclick="sortBy('created_at')">
Created at
<span class="sort-indicator"></span>
</th>
<th class="short-link">Short link</th>
<th class="views">Views</th>
<th class="views sortable" onclick="sortBy('visit_count')">
Views
<span class="sort-indicator"></span>
</th>
<th class="actions"></th>
</tr>
</thead>
</thead>
Loading