Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Loosie94 authored Nov 8, 2023
0 parents commit cd4d58b
Show file tree
Hide file tree
Showing 273 changed files with 24,712 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .directus/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
The .directus directory holds a sample Docker Compose file that you can use to quickly spin up a local version of
AgencyOS.

It also contains a `run-scripts` with some of the Run Script operation scripts using in the Directus instance > Flows >
Operations.
59 changes: 59 additions & 0 deletions .directus/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
version: '3'
services:
database:
image: postgis/postgis:13-master
# Required when running on platform other than amd64, like Apple M1/M2:
# platform: linux/amd64
ports:
- 5432:5432
volumes:
- ./data/database:/var/lib/postgresql/data
environment:
POSTGRES_USER: 'directus'
POSTGRES_PASSWORD: 'directus'
POSTGRES_DB: 'directus'

cache:
image: redis:6

directus:
image: directus/directus:10.6.3
ports:
- 8055:8055
volumes:
- ./uploads:/directus/uploads
# If you want to load extensions from the host
- ./extensions:/directus/extensions
depends_on:
- cache
- database
environment:
KEY: '255d861b-5ea1-5996-9aa3-922530ec40b1'
SECRET: '6116487b-cda1-52c2-b5b5-c8022c45e263'

DB_CLIENT: 'pg'
DB_HOST: 'database'
DB_PORT: '5432'
DB_DATABASE: 'directus'
DB_USER: 'directus'
DB_PASSWORD: 'directus'

CACHE_ENABLED: 'false'
CACHE_STORE: 'redis'
REDIS: 'redis://cache:6379'

ADMIN_EMAIL: 'admin@example.com'
ADMIN_PASSWORD: 'd1r3ctu5'

# These is helpful for local developement but should probably be removed in production
CORS_ENABLED: 'true'
REFRESH_TOKEN_COOKIE_DOMAIN: 'localhost'
EXTENSIONS_AUTO_RELOAD: 'true'

WEBSOCKETS_ENABLED: 'true'
# The default config prevents importing files from 0.0.0.0. See https://docs.directus.io/self-hosted/config-options.html#security . This can be removed in production but in local development it is recommended to keep it so you can import logos from Organization > website.
IMPORT_IP_DENY_LIST: ''

# Make sure to set this in production
# (see https://docs.directus.io/self-hosted/config-options#general)
# PUBLIC_URL: 'https://directus.example.com'
32 changes: 32 additions & 0 deletions .directus/run-scripts/calculate-invoice-items.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module.exports = async function (data) {
const item = { ...data.invoice_items, ...data.$trigger.payload };

// Basic validation for unit_price and quantity
if (!item.unit_price || !item.quantity) {
throw new Error('Missing or invalid unit_price or quantity.');
}

// Calculate line_amount
const lineAmount = parseFloat(item.unit_price) * parseFloat(item.quantity);
if (isNaN(lineAmount)) {
throw new Error('Error calculating line_amount.');
}
item.line_amount = lineAmount;

// Validate and calculate tax_amount if tax_rate is provided
if (item.tax_rate && item.tax_rate.rate) {
const taxRateId = item.tax_rate.id;
const taxAmount = (lineAmount * parseFloat(item.tax_rate.rate)) / 100;
if (isNaN(taxAmount)) {
throw new Error('Error calculating tax_amount.');
}
item.tax_amount = taxAmount;

delete item.tax_rate;
item.tax_rate = taxRateId;
} else {
item.tax_amount = 0;
}

return item;
};
52 changes: 52 additions & 0 deletions .directus/run-scripts/calculate-invoice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
function calculateInvoiceTotals(invoice) {
if (!invoice || !Array.isArray(invoice.line_items)) {
throw new Error('Invalid invoice data format.');
}

let subtotal = 0;
let totalTax = 0;

for (let item of invoice.line_items) {
if (item.line_amount) {
subtotal += parseFloat(item.line_amount);
}

if (item.tax_amount) {
totalTax += parseFloat(item.tax_amount);
}
}

let changes = {};

if (invoice.subtotal !== subtotal.toFixed(2)) {
changes.subtotal = subtotal.toFixed(2);
}

if (invoice.total_tax !== totalTax.toFixed(2)) {
changes.total_tax = totalTax.toFixed(2);
}

let total = (subtotal + totalTax).toFixed(2);
if (invoice.total !== total) {
changes.total = total;
}

if (Object.keys(changes).length === 0) {
return null; // No changes detected
}

return changes;
}

module.exports = async function (data) {
try {
const changes = calculateInvoiceTotals(data.invoice);
if (changes) {
return changes;
} else {
throw new Error('No changes necessary');
}
} catch (error) {
throw new Error(error.message);
}
};
19 changes: 19 additions & 0 deletions .directus/run-scripts/extract-domain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
function extractDomain(inputUrl) {
// Regular expression to extract domain from URL
const regex = /^(?:https?:\/\/)?(?:www\.)?([^\/?#]+)(?:[\/?#]|$)/i;
const matches = inputUrl.match(regex);

// Return the matched domain or undefined if not found
return matches && matches[1];
}

module.exports = function (data) {
const website = data.$trigger.payload.website;

const domain = extractDomain(website);

if (!domain) {
throw new Error('Unable to properly determine the domain for this website');
}
return domain;
};
20 changes: 20 additions & 0 deletions .directus/run-scripts/handle-trigger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// This function is called when used with an Event Hook - Action (Non-Blocking) - Trigger for a flow. It is used to extract the key from the trigger object because the syntax return from Directus can be different depending on the type of trigger. For example, the trigger object for a "New Item" trigger is different than the trigger object for a "New Item in View" trigger. This function is used to extract the key from the trigger object regardless of the type of trigger.

function extractKey(obj) {
if (obj.key) {
// If "key" property exists, return its value
return obj.key;
} else if (obj.keys && Array.isArray(obj.keys) && obj.keys.length > 0) {
// If "keys" property exists, is an array, and has at least one element, return the first element
return obj.keys[0];
} else {
// If neither condition is met, return null or throw an error
throw new Error('Key not found');
}
}

module.exports = async function (data) {
const key = extractKey(data.$trigger);

return key;
};
15 changes: 15 additions & 0 deletions .directus/run-scripts/interpolate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
function interpolate(str, params) {
const names = Object.keys(params);
const vals = Object.values(params);
return new Function(...names, `return \`${str}\`;`)(...vals);
}

module.exports = async function (data) {
const test = 'Bryant';
const email = data.get_email_template[0];
const body = email.body;
const result = interpolate(body, { test });

// Do something...
return result;
};
78 changes: 78 additions & 0 deletions .directus/run-scripts/validate-schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
function validateSchemaNames(schema, fieldsArray) {
const fieldNames = fieldsArray.map((field) => field.field);
const invalidNames = [];

schema.forEach((item) => {
if (item.name && !fieldNames.includes(item.name)) {
invalidNames.push(item.name);
}
if (item.children && Array.isArray(item.children)) {
item.children.forEach((child) => {
if (child.name && !fieldNames.includes(child.name)) {
invalidNames.push(child.name);
}
});
}
});

if (invalidNames.length > 0) {
throw new Error(`Form schema field names don't match collection field names: ${invalidNames.join(', ')}`);
}
}

const form_schema = [
{
name: 'first_name',
type: 'text',
label: 'First Name',
placeholder: 'John',
help: null,
validation: 'required',
width: '50',
},
{
name: 'last_name',
type: 'text',
label: 'Last Name',
validation: 'required',
width: '50',
},
{
name: 'email',
type: 'text',
label: 'Email',
placeholder: 'john@example.com',
validation: 'required',
width: '100',
},
{
name: 'organization',
type: 'text',
label: 'Company',
help: `What's the name of your company / organization?`,
width: '100',
conditionalIf: '$get(first_name).value',
},
{
name: 'signature',
type: 'signature',
label: 'Signature',
help: `Please sign your name above.`,
width: '100',
validation: 'required',
options: ['type', 'draw', 'upload'],
},
{
name: 'esignature_agreement',
type: 'checkbox',
label: 'I agree that my electronic signature is as valid and legally binding as a handwritten signature.',
validation: 'required',
width: '100',
},
];

module.exports = async function (data) {
validateSchemaNames(form_schema, data.get_fields);

return;
};
11 changes: 11 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
root=true

[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = tab
trim_trailing_whitespace = true

[*.{yml,yaml}]
indent_style = space
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Directus Setup for Nuxt app
# Directus Cloud urls typically look like this https://your-instance.directus.app or if you're running a local instance of Directus, it will look like this http://localhost:8055 or http://0.0.0.0:8055 or http://127.0.0.1:8055/ (depending on your setup)

DIRECTUS_URL="https://your-instance.directus.app"
DIRECTUS_SERVER_TOKEN="your_directus_server_token_for_server_only_routes"
SITE_URL="http://localhost:3000"

# Stripe Setup (If you want to allow payments within the portal)
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxx
STRIPE_PUBLISHABLE_KEY=pk_xxxxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxx
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
Loading

0 comments on commit cd4d58b

Please sign in to comment.