@tanglemedia/directus-extension-bundle-page-views
Version:
A Directus extension bundle that adds a page views collection and dashboard to track page views.
495 lines (486 loc) • 8.66 kB
JavaScript
import crypto from "crypto";
const DASHBOARD_UUID = "795caeab-3f39-4031-a1ed-631d3b127fb4";
const COLLECTION_NAME = "tngl_page_views";
// create function to generate a uuidv4
function generateUUIDv4() {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
(c ^ ((crypto.randomBytes(1)[0] & 15) >> (c / 4))).toString(16)
);
}
// preset that applies to any user or role
const PANELS = [
{
name: "Top 10 Sources",
icon: null,
color: null,
show_header: true,
note: null,
type: "metric-list",
position_x: 57,
position_y: 29,
width: 32,
height: 22,
options: JSON.stringify({
limit: 10,
filter: {
_and: [
{
date_created: {
_gt: "$NOW(-{{ time_range }})",
},
},
{
environment: {
_eq: "Production",
},
},
{
source: {
_ncontains: process.env.SITE_BASE_URL,
},
}
],
},
collection: COLLECTION_NAME,
groupByField: "source",
aggregateField: "source",
aggregateFunction: "count",
}),
},
{
name: "Top 10 Visited Pages",
icon: null,
color: null,
show_header: true,
note: null,
type: "metric-list",
position_x: 57,
position_y: 7,
width: 32,
height: 22,
options: JSON.stringify({
limit: 10,
filter: {
_and: [
{
date_created: {
_gt: "$NOW(-{{ time_range }})",
},
},
{
environment: {
_eq: "Production",
},
},
],
},
collection: COLLECTION_NAME,
groupByField: "endpoint",
aggregateField: "endpoint",
aggregateFunction: "count",
}),
},
{
name: "Total Unique Visitors",
icon: null,
color: null,
show_header: true,
note: null,
type: "metric",
position_x: 1,
position_y: 40,
width: 14,
height: 11,
options: JSON.stringify({
fn: "count",
max: 100,
field: "ip_address",
filter: {
_and: [
{
date_created: {
_gt: "$NOW(-{{ time_range }})",
},
},
{
environment: {
_eq: "Production",
},
},
],
},
suffix: null,
fontSize: "48px",
function: "countDistinct",
notation: "compact",
sortField: "ip_address",
collection: COLLECTION_NAME,
maximumFractionDigits: 1,
minimumFractionDigits: 1,
}),
},
{
name: "Unique Visitors",
icon: "today",
color: null,
show_header: true,
note: null,
type: "time-series",
position_x: 15,
position_y: 29,
width: 42,
height: 22,
options: JSON.stringify({
max: null,
min: 0,
color: "#6644FF",
range: "{{ time_range }}",
filter: {
_and: [
{
environment: {
_eq: "Production",
},
},
],
},
function: "countDistinct",
dateField: "date_created",
precision: "day",
collection: COLLECTION_NAME,
valueField: "ip_address",
}),
},
{
name: "Top Locations Viewed",
icon: null,
color: null,
show_header: false,
note: null,
type: "choropleth-map-panel",
position_x: 1,
position_y: 51,
width: 56,
height: 44,
options: JSON.stringify({
filter: {
_and: [
{
date_created: {
_gt: "$NOW(-{{ time_range }})",
},
},
{
environment: {
_eq: "Production",
},
},
],
},
collection: COLLECTION_NAME,
countryField: "country_code",
aggregateField: "ip_address",
aggregateFunction: "count",
}),
},
{
name: "Top 10 Countries",
icon: null,
color: null,
show_header: true,
note: null,
type: "metric-list",
position_x: 57,
position_y: 51,
width: 32,
height: 22,
options: JSON.stringify({
limit: 10,
filter: {
_and: [
{
date_created: {
_gt: "$NOW(-{{ time_range }})",
},
},
{
environment: {
_eq: "Production",
},
},
],
},
collection: COLLECTION_NAME,
groupByField: "country_name",
aggregateField: "country_code",
aggregateFunction: "count",
}),
},
{
name: "Time Range",
icon: null,
color: null,
show_header: true,
note: null,
type: "variable",
position_x: 1,
position_y: 1,
width: 88,
height: 6,
options: JSON.stringify({
type: "string",
field: "time_range",
inter: "select-dropdown",
options: {
choices: [
{
text: "Past Year",
value: "12 months",
},
{
text: "Past 6 Months",
value: "6 months",
},
{
text: "Past 3 Months",
value: "3 months",
},
{
text: "Past 2 Weeks",
value: "2 weeks",
},
{
text: "Past Month",
value: "1 month",
},
{
text: "Past Week",
value: "1 week",
},
{
text: "Today",
value: "1 day",
},
],
placeholder: "Select Filter",
},
defaultValue: "1 month",
}),
},
{
name: "Device Usage",
icon: null,
color: null,
show_header: true,
note: null,
type: "pie-chart",
position_x: 1,
position_y: 29,
width: 14,
height: 11,
options: JSON.stringify({
fn: "count",
max: 100,
field: "ip_address",
column: "device",
filter: {
_and: [
{
date_created: {
_gt: "$NOW(-{{ time_range }})",
},
},
{
environment: {
_eq: "Production",
},
},
],
},
legend: "right",
suffix: null,
fontSize: "48px",
function: "count",
notation: "compact",
sortField: "device",
collection: COLLECTION_NAME,
showLabels: true,
maximumFractionDigits: 1,
minimumFractionDigits: 1,
}),
},
{
name: "Distinct Pages Viewed",
icon: null,
color: null,
show_header: true,
note: null,
type: "metric",
position_x: 1,
position_y: 18,
width: 14,
height: 11,
options: JSON.stringify({
fn: "count",
field: "endpoint",
filter: {
_and: [
{
date_created: {
_gt: "$NOW(-{{ time_range }})",
},
},
{
environment: {
_eq: "Production",
},
},
],
},
suffix: null,
fontSize: "48px",
function: "countDistinct",
notation: "compact",
sortField: "endpoint",
collection: COLLECTION_NAME,
showPercentage: false,
maximumFractionDigits: 1,
minimumFractionDigits: 1,
}),
},
{
name: "Total Pages Viewed",
icon: null,
color: null,
show_header: true,
note: null,
type: "metric",
position_x: 1,
position_y: 7,
width: 14,
height: 11,
options: JSON.stringify({
field: "endpoint",
filter: {
_and: [
{
date_created: {
_gt: "$NOW(-{{ time_range }})",
},
},
{
environment: {
_eq: "Production",
},
},
],
},
suffix: null,
fontSize: "48px",
function: "count",
notation: "compact",
sortField: "endpoint",
collection: COLLECTION_NAME,
maximumFractionDigits: 1,
minimumFractionDigits: 1,
}),
},
{
name: "Page Views",
icon: "today",
color: null,
show_header: true,
note: null,
type: "time-series",
position_x: 15,
position_y: 7,
width: 42,
height: 22,
options: JSON.stringify({
max: null,
min: 0,
color: "#6644FF",
range: "{{ time_range }}",
filter: {
_and: [
{
environment: {
_eq: "Production",
},
},
],
},
function: "count",
dateField: "date_created",
precision: "day",
collection: COLLECTION_NAME,
valueField: "ip_address",
}),
},
{
name: "Top 10 Languages",
icon: null,
color: null,
show_header: true,
note: null,
type: "metric-list",
position_x: 57,
position_y: 73,
width: 32,
height: 22,
options: JSON.stringify({
limit: 10,
filter: {
_and: [
{
date_created: {
_gt: "$NOW(-{{ time_range }})",
},
},
{
environment: {
_eq: "Production",
},
},
],
},
collection: COLLECTION_NAME,
groupByField: "language_name",
aggregateField: "language_name",
aggregateFunction: "count",
}),
},
];
export async function up(knex) {
// addd Website Analytics Dashboard
await knex("directus_dashboards").insert({
id: DASHBOARD_UUID,
name: "Website Analytics",
icon: "assessment",
note: "Metrics for Website Analytics",
color: null,
});
// add Website Analytics panels
const newPanels = PANELS.map((panel) => {
return knex("directus_panels").insert({
...panel,
dashboard: DASHBOARD_UUID,
id: generateUUIDv4(),
});
});
await Promise.all(newPanels);
}
export async function down(knex) {
// remove Website Analytics panels
await knex("directus_panels")
.where({
dashboard: DASHBOARD_UUID,
})
.del();
// remove Website Analytics Dashboard
await knex("directus_dashboards")
.where({
id: DASHBOARD_UUID,
})
.del();
}