Skip to content
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
183 changes: 183 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
name: Deploy to Vapor and Frontend

on:
push:
branches:
- main
- develop
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy to'
required: true
default: 'staging'
type: choice
options:
- staging
- production
test_mode:
description: 'Run in test mode (no actual deployment)'
required: false
default: false
type: boolean

jobs:
backend:
name: Deploy Backend
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.3
tools: composer:v2
coverage: none

- name: Prepare Laravel Environment
working-directory: ./backend
run: |
mkdir -p bootstrap/cache
chmod -R 775 bootstrap/cache

- name: Prepare HTMLPurifier Cache Directory
working-directory: ./backend
run: |
mkdir -p storage/app/htmlpurifier
chmod -R 775 storage/app/htmlpurifier

- name: Install Dependencies
working-directory: ./backend
run: composer install --no-dev --no-progress --no-scripts --optimize-autoloader

- name: Install Vapor CLI
run: composer global require laravel/vapor-cli

- name: Set Deployment Environment
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "VAPOR_ENV=${{ github.event.inputs.environment }}" >> "$GITHUB_ENV"
echo "TEST_MODE=${{ github.event.inputs.test_mode }}" >> "$GITHUB_ENV"
elif [[ "${{ github.ref_name }}" == "develop" ]]; then
echo "VAPOR_ENV=staging" >> "$GITHUB_ENV"
echo "TEST_MODE=false" >> "$GITHUB_ENV"
else
echo "VAPOR_ENV=production" >> "$GITHUB_ENV"
echo "TEST_MODE=false" >> "$GITHUB_ENV"
fi

- name: Log Branch and Environment
run: |
echo "🚀 Deploying branch ${{ github.ref_name }} to Vapor environment: ${{ env.VAPOR_ENV }}"
echo "🧪 Test mode: ${{ env.TEST_MODE }}"

- name: Validate Deployment Configuration
working-directory: ./backend
run: |
if [[ "${{ env.TEST_MODE }}" == "true" ]]; then
echo "✅ TEST MODE: Would deploy to ${{ env.VAPOR_ENV }} environment"
echo "vapor deploy ${{ env.VAPOR_ENV }} --dry-run"
exit 0
fi

- name: Deploy to Vapor
working-directory: ./backend
run: vapor deploy ${{ env.VAPOR_ENV }}
env:
VAPOR_API_TOKEN: ${{ secrets.VAPOR_API_TOKEN }}

frontend:
name: Deploy Frontend
runs-on: ubuntu-latest
needs: backend

steps:
- uses: actions/checkout@v3

- name: Set Deployment Environment
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
if [[ "${{ github.event.inputs.environment }}" == "staging" ]]; then
echo "DO_APP_ID=${{ secrets.DIGITALOCEAN_STAGING_APP_ID }}" >> "$GITHUB_ENV"
else
echo "DO_APP_ID=${{ secrets.DIGITALOCEAN_PRODUCTION_APP_ID }}" >> "$GITHUB_ENV"
fi
echo "TEST_MODE=${{ github.event.inputs.test_mode }}" >> "$GITHUB_ENV"
elif [[ "${{ github.ref_name }}" == "develop" ]]; then
echo "DO_APP_ID=${{ secrets.DIGITALOCEAN_STAGING_APP_ID }}" >> "$GITHUB_ENV"
echo "TEST_MODE=false" >> "$GITHUB_ENV"
else
echo "DO_APP_ID=${{ secrets.DIGITALOCEAN_PRODUCTION_APP_ID }}" >> "$GITHUB_ENV"
echo "TEST_MODE=false" >> "$GITHUB_ENV"
fi

- name: Log Environment Settings
run: |
echo "🚀 Deploying frontend to DigitalOcean App: ${{ env.DO_APP_ID }}"
echo "🧪 Test mode: ${{ env.TEST_MODE }}"

- name: Validate Deployment Configuration (Test Mode)
if: env.TEST_MODE == 'true'
run: |
echo "✅ TEST MODE: Would trigger deployment for DigitalOcean App: ${{ env.DO_APP_ID }}"
echo "curl -X POST 'https://api.digitalocean.com/v2/apps/${{ env.DO_APP_ID }}/deployments'"
exit 0

- name: Trigger Deployment on DigitalOcean
if: env.TEST_MODE != 'true'
id: trigger_deployment
run: |
RESPONSE=$(curl -s -o response.json -w "%{http_code}" -X POST "https://api.digitalocean.com/v2/apps/$DO_APP_ID/deployments" \
-H "Authorization: Bearer ${{ secrets.DIGITALOCEAN_API_TOKEN }}" \
-H "Content-Type: application/json")

if [ "$RESPONSE" -ne 201 ] && [ "$RESPONSE" -ne 200 ]; then
ERROR_MSG=$(jq -r '.message // "Unknown error occurred."' response.json)
echo "❌ Failed to trigger deployment. HTTP Status: $RESPONSE. Error: $ERROR_MSG"
exit 1
fi

DEPLOYMENT_ID=$(jq -r '.deployment.id' response.json)
if [ "$DEPLOYMENT_ID" == "null" ]; then
echo "❌ Failed to extract deployment ID."
exit 1
fi

echo "::add-mask::$DEPLOYMENT_ID"
echo "✅ Deployment triggered successfully."

echo "deployment_id=$DEPLOYMENT_ID" >> "$GITHUB_ENV"

- name: Poll Deployment Status
if: env.TEST_MODE != 'true'
run: |
MAX_RETRIES=60
SLEEP_TIME=10
COUNTER=0

while [ $COUNTER -lt $MAX_RETRIES ]; do
RESPONSE=$(curl -s -X GET "https://api.digitalocean.com/v2/apps/$DO_APP_ID/deployments/${{ env.deployment_id }}" \
-H "Authorization: Bearer ${{ secrets.DIGITALOCEAN_API_TOKEN }}" \
-H "Content-Type: application/json")

STATUS=$(echo "$RESPONSE" | jq -r '.deployment.phase')

echo "🔄 Deployment Status: $STATUS"

if [ "$STATUS" == "ACTIVE" ]; then
echo "✅ Deployment completed successfully."
exit 0
elif [[ "$STATUS" == "FAILED" || "$STATUS" == "CANCELLED" ]]; then
echo "❌ Deployment failed or was cancelled."
exit 1
fi

COUNTER=$((COUNTER + 1))
echo "⏳ Retrying in $SLEEP_TIME seconds... ($COUNTER/$MAX_RETRIES)"
sleep $SLEEP_TIME
done

echo "⏰ Deployment timed out."
exit 1
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
frontend/.env
backend/.env
todo.md

.vercel
8 changes: 5 additions & 3 deletions Dockerfile.all-in-one
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,19 @@ RUN apk add --no-cache nodejs yarn nginx supervisor
COPY --from=node-frontend /app/frontend /app/frontend

COPY ./backend /app/backend
RUN chown -R www-data:www-data /app/backend \
RUN mkdir -p /app/backend/bootstrap/cache \
&& mkdir -p /app/backend/storage \
&& chown -R www-data:www-data /app/backend \
&& find /app/backend -type d -exec chmod 755 {} \; \
&& find /app/backend -type f -exec chmod 644 {} \; \
&& chmod -R 777 /app/backend/storage /app/backend/bootstrap/cache \
&& chmod -R 755 /app/backend/storage /app/backend/bootstrap/cache \
&& composer install --working-dir=/app/backend \
--ignore-platform-reqs \
--no-interaction \
--no-dev \
--optimize-autoloader \
--prefer-dist \
&& chmod -R 777 /app/backend/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer
&& chmod -R 755 /app/backend/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer

COPY ./docker/all-in-one/nginx/nginx.conf /etc/nginx/nginx.conf
COPY ./docker/all-in-one/supervisor/supervisord.conf /etc/supervisord.conf
Expand Down
70 changes: 45 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
<img src="https://hievents-public.s3.us-west-1.amazonaws.com/website/hi-events-rainbow.png?v=1" alt="Hi.Events Logo" width="200px">
</p>


<h3 align="center">Hi.Events</h3>

<p align="center">
<a href="https://demo.hi.events/event/1/dog-conf-2030">Demo Event 🌟</a> • <a href="https://hi.events?utm_source=gh-readme">Website 🌎</a> • <a href="https://hi.events/docs">Documentation 📄</a> • <a href="https://hi.events/docs/getting-started?utm_source=gh-readme">Installation ⚙️</a>
<a href="https://demo.hi.events/event/1/dog-conf-2030">Demo Event 🌟</a> • <a href="https://hi.events?utm_source=gh-readme">Website 🌎</a> • <a href="https://hi.events/docs?utm_source=gh-readme">Documentation 📄</a> • <a href="https://hi.events/docs/getting-started?utm_source=gh-readme">Installation ⚙️</a>
</p>

<h3 align="center">
Expand Down Expand Up @@ -59,34 +58,55 @@
<a href="https://hi.events">Hi.Events</a> is a feature-rich, self-hosted event management and ticketing platform. From conferences to club nights,
Hi.Events is designed to help you create, manage, and sell tickets for events of all sizes.

<img alt="Hi.Events self-hosted ticket selling dashboard" src="https://hievents-public.s3.us-west-1.amazonaws.com/website/dashboard-screenshot.png"/>
<img alt="Hi.Events self-hosted ticket selling dashboard" src="https://hievents-public.s3.us-west-1.amazonaws.com/website/github-readme-screenshot.png"/>
<caption>Generated using <a href="https://screenshot.rocks?utm_source=hi.events-readme">Screenshot Rocks</a></caption>

## 🌟 Features

<a href="https://hi.events">Hi.Events</a> is packed with features to streamline your event management and ticketing:

- 📊 **Event Analytics:** Gain deep insights into event performance and ticket sales.
- 🎟 **Embeddable Ticket Widget:** Easily integrate ticket sales into any website.
- 🖥 **Customizable Event Homepages:** Create eye-catching event pages with flexible design options.
- 🔑 **Intuitive Check-In Tools:** Easily check in attendees at the door with Hi.Events' QR code check-in tool.
- 💬 **Event Messaging Tools:** Message attendees with important updates and reminders.
- 📝 **Custom Order Forms:** Collect attendee information with tailored questions at checkout.
- 🎫 **Multiple Ticket Types:** Free, paid, donation, or tiered ticket types.
- 💸 **Versatile Promo Codes:** Highly versatile discount codes. Pre-sale access, multiple discount options.
- 💰 **Instant Payouts:** Enjoy instant payouts with seamless Stripe integration.
- 🧾 **Tax and Fee Configuration:** Add tax and fees on a per-ticket basis.
- 📦 **Data Exports:** Export attendee and order data to XLSX or CSV.
- 💻 **REST API:** Full-featured REST API for custom integrations.
- 🔍 **SEO Tools:** Customize SEO settings for each event.
- 🛒 **Beautiful Checkout Process:** Ensure a smooth, beautiful checkout experience.
- 🔐 **Role-Based Access:** Support for multiple user roles.
- 💻 **Online Event Support:** Offer online event instructions and links.
- ⏪ **Full and Partial Refund Support:** Manage full and partial refunds with ease.
- 📧 **Email Notifications:** Keep attendees informed with automated email notifications.
- 📱 **Mobile-Responsive:** Enjoy a seamless experience on any device.
- 🌐 **Multi-Language Support:** Support for multiple languages (English, Português, Español, 中文 (Zhōngwén), Deutsch, Français)
- 🔋 **Advanced Capacity Management:** Set capacity limits across multiple ticket types.
- 🎉 **And much more!**
### 🎟 Ticketing & Product Sales
- **Multiple Ticket Types:** Free, Paid, Donation, and Tiered tickets.
- **Capacity Management:** Set event-wide or ticket-specific limits.
- **Capacity Assignments:** Manage shared capacity across multiple ticket types.
- **Promo Codes:** Discount codes for pre-sale access and special offers.
- **Product Sales:** Sell event-related products (e.g., t-shirts, add-ons).
- **Taxes & Fees:** Apply custom taxes and fees per product or order.

### 🏆 Event Management & Customization
- **Event Dashboard:** Real-time revenue, ticket sales, and attendee analytics.
- **Homepage Designer:** Customize event pages with a live preview editor.
- **Embeddable Ticket Widget:** Add a seamless ticketing experience to your website.
- **SEO Tools:** Customize event metadata for better search visibility.
- **Product Categories:** Organize products and tickets with category management.
- **Offline Event Support:** Provide instructions for physical events.

### 📧 Attendee & Order Management
- **Custom Checkout Forms:** Collect attendee details with tailored questions.
- **Attendee Management:** Search, edit, cancel, and message attendees.
- **Order Management:** Refund, cancel, and resend order details easily.
- **Bulk Messaging:** Email or message specific ticket holders.
- **Data Exports:** Export attendees and orders to CSV/XLSX.

### 📱 Mobile-Friendly & Check-In Tools
- **QR Code Check-In:** Web-based and mobile-friendly check-in tool.
- **Check-In Lists:** Generate and share access-controlled check-in lists.
- **Multi-User Access:** Role-based access control for event staff.

### 🔧 Integrations & Automation
- **Webhooks Support:** Automate tasks with Zapier, IFTTT, Make, or CRM integrations.
- **Stripe Connect Integration:** Organizers get instant payouts.

### 📊 Advanced Features
- **Multi-Language Support:** English, Deutsch, Español, Português, Français, 中文 (Zhōngwén), and more.
- **Partial & Full Refunds:** Manage refunds with detailed order tracking.
- **Role-Based Access Control:** Multiple user roles with permission management.
- **REST API:** Full API access for custom integrations.
- **Invoicing System:** Generate and send invoices with tax details, payment terms, and due dates.
- **Offline Payment Support:** Enable bank transfers, cash payments, or custom payment methods.
- **Event Archive:** Archive past events to keep the dashboard organized.
- **Advanced Ticket Locking:** Lock tickets behind promo codes or access restrictions.
- **Advanced Reporting:** Daily sales, tax breakdowns, product sales, and promo code usage reports.

## 🚀 Getting Started

Expand Down
4 changes: 3 additions & 1 deletion backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ APP_SAAS_MODE_ENABLED=false
APP_SAAS_STRIPE_APPLICATION_FEE_PERCENT=1.5
APP_HOMEPAGE_VIEWS_UPDATE_BATCH_SIZE=8
APP_DISABLE_REGISTRATION=false
APP_STRIPE_CONNECT_ACCOUNT_TYPE=express
[email protected]

STRIPE_PUBLIC_KEY=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=

CORS_ALLOWED_ORIGINS=http://localhost:5173
CORS_ALLOWED_ORIGINS=*

LOG_CHANNEL=stderr
LOG_DEPRECATIONS_CHANNEL=null
Expand Down
2 changes: 2 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
.env
.env.backup
.env.production
.env.staging
.phpunit.result.cache
Homestead.json
Homestead.yaml
Expand All @@ -19,3 +20,4 @@ yarn-error.log
/.vscode
.idea
/app-back
bootstrap/cache
16 changes: 16 additions & 0 deletions backend/app/DomainObjects/AccountConfigurationDomainObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace HiEvents\DomainObjects;

class AccountConfigurationDomainObject extends Generated\AccountConfigurationDomainObjectAbstract
{
public function getFixedApplicationFee(): float
{
return $this->getApplicationFees()['fixed'] ?? config('app.default_application_fee_fixed');
}

public function getPercentageApplicationFee(): float
{
return $this->getApplicationFees()['percentage'] ?? config('app.default_application_fee_percentage');
}
}
24 changes: 24 additions & 0 deletions backend/app/DomainObjects/AccountDomainObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,30 @@

namespace HiEvents\DomainObjects;

use HiEvents\DomainObjects\DTO\AccountApplicationFeeDTO;

class AccountDomainObject extends Generated\AccountDomainObjectAbstract
{
private ?AccountConfigurationDomainObject $configuration = null;

public function getApplicationFee(): AccountApplicationFeeDTO
{
/** @var AccountConfigurationDomainObject $applicationFee */
$applicationFee = $this->getConfiguration();

return new AccountApplicationFeeDTO(
$applicationFee->getPercentageApplicationFee(),
$applicationFee->getFixedApplicationFee()
);
}

public function getConfiguration(): ?AccountConfigurationDomainObject
{
return $this->configuration;
}

public function setConfiguration(AccountConfigurationDomainObject $configuration): void
{
$this->configuration = $configuration;
}
}
7 changes: 7 additions & 0 deletions backend/app/DomainObjects/ApplicationFeeDomainObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace HiEvents\DomainObjects;

class ApplicationFeeDomainObject extends Generated\ApplicationFeeDomainObjectAbstract
{
}
Loading
Loading