Commit de2cc233 authored by Lamahewa D's avatar Lamahewa D

Completed backend code

parent 51e1db54
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
APP_NAME=Lumen
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
APP_TIMEZONE=UTC
LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL=
DB_CONNECTION=mysql
DB_HOST=dcsrp-db-01.cvqssef8vzgx.ap-southeast-1.rds.amazonaws.com
DB_PORT=3306
DB_DATABASE=dcsrp_db
DB_USERNAME=dcsrp_db
DB_PASSWORD=BJEutn4UT5z9Vfye6LWe7kXK
JWT_SECRET=cb4cf82d320a462db851a5fccd06366b
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
/vendor
/.idea
Homestead.json
Homestead.yaml
.phpunit.result.cache
php:
preset: laravel
disabled:
- unused_use
js: true
css: true
default: run
run:
php -S localhost:9595 -t public
\ No newline at end of file
# Lumen PHP Framework
[![Build Status](https://travis-ci.org/laravel/lumen-framework.svg)](https://travis-ci.org/laravel/lumen-framework)
[![Total Downloads](https://img.shields.io/packagist/dt/laravel/lumen-framework)](https://packagist.org/packages/laravel/lumen-framework)
[![Latest Stable Version](https://img.shields.io/packagist/v/laravel/lumen-framework)](https://packagist.org/packages/laravel/lumen-framework)
[![License](https://img.shields.io/packagist/l/laravel/lumen)](https://packagist.org/packages/laravel/lumen-framework)
Laravel Lumen is a stunningly fast PHP micro-framework for building web applications with expressive, elegant syntax. We believe development must be an enjoyable, creative experience to be truly fulfilling. Lumen attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as routing, database abstraction, queueing, and caching.
> **Note:** In the years since releasing Lumen, PHP has made a variety of wonderful performance improvements. For this reason, along with the availability of [Laravel Octane](https://laravel.com/docs/octane), we no longer recommend that you begin new projects with Lumen. Instead, we recommend always beginning new projects with [Laravel](https://laravel.com).
## Official Documentation
Documentation for the framework can be found on the [Lumen website](https://lumen.laravel.com/docs).
## Contributing
Thank you for considering contributing to Lumen! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
## Security Vulnerabilities
If you discover a security vulnerability within Lumen, please send an e-mail to Taylor Otwell at taylor@laravel.com. All security vulnerabilities will be promptly addressed.
## License
The Lumen framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Laravel\Lumen\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
//
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
//
}
}
<?php
namespace App\Events;
use Illuminate\Queue\SerializesModels;
abstract class Event
{
use SerializesModels;
}
<?php
namespace App\Events;
class ExampleEvent extends Event
{
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
}
<?php
namespace App\Exceptions;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Validation\ValidationException;
use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that should not be reported.
*
* @var array
*/
protected $dontReport = [
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
ValidationException::class,
];
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Throwable $exception
* @return void
*
* @throws \Exception
*/
public function report(Throwable $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Throwable $exception
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*
* @throws \Throwable
*/
public function render($request, Throwable $exception)
{
return parent::render($request, $exception);
}
}
<?php
namespace App\Http\Controllers;
use App\Models\Staff;
use Firebase\JWT\JWT;
use Illuminate\Http\Request;
class AuthController extends Controller {
public function login(Request $request) {
$User = Staff::where("email", $request->json()->get("email"))->first();
if ( $User != NULL ):
if ( password_verify($request->json()->get("password"), $User->password) ):
/**
* Generate JWT token for the logged in user to
* use in future requests to commiunicate with
* backend API
*/
$jwt = JWT::encode([
"sub" => $User->id,
"email" => $User->email
], env("JWT_SECRET"), "HS256");
return response()->json([ "status" => "success", "token" => $jwt ]);
else:
return response()->json([ "status" => "failed", "error" => "Wrong password" ]);
endif;
else:
return response()->json([ "status" => "failed", "error" => "No user account found" ]);
endif;
}
}
<?php
namespace App\Http\Controllers;
use Laravel\Lumen\Routing\Controller as BaseController;
class Controller extends BaseController {}
<?php
namespace App\Http\Controllers;
class HomeController extends Controller {
public function home() {
return response()->json([ "status" => "success" ]);
}
}
<?php
namespace App\Http\Controllers;
use App\Models\Tickets;
use Illuminate\Http\Request;
class TicketController extends Controller {
public function get(Request $request) {
/**
* Get all the tickets from database to display
* in the frontend. Limit and offset the records
* using the page number query parameter
*/
$limit = ($request->query("limit")) ? $request->query("limit") : 10;
$page = ($request->query("page")) ? $request->query("page") : 0;
$Tickets = Tickets::with(["call"])->skip($page*$limit)->limit($limit)->orderBy("id", "desc")->get();
$data = array(
"tickets" => $Tickets,
"pagination" => array(
"limit" => $limit,
"page" => $request->query("page")
)
);
return response()->json([ "status" => "success", "data" => $data ]);
}
public function callback_post(Request $request) {
/**
* Create ticket with the information received by
* the middleware to initiate customer support flow
* from the backend application
*/
Tickets::create([
"call_id" => $request->json()->get("call")
]);
return response()->json([ "status" => "success" ]);
}
}
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Factory as Auth;
class Authenticate
{
/**
* The authentication guard factory instance.
*
* @var \Illuminate\Contracts\Auth\Factory
*/
protected $auth;
/**
* Create a new middleware instance.
*
* @param \Illuminate\Contracts\Auth\Factory $auth
* @return void
*/
public function __construct(Auth $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $guard
* @return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if ($this->auth->guard($guard)->guest()) {
return response('Unauthorized.', 401);
}
return $next($request);
}
}
<?php
namespace App\Http\Middleware;
use Closure;
class ExampleMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
return $next($request);
}
}
<?php
namespace App\Jobs;
class ExampleJob extends Job
{
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
//
}
}
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
abstract class Job implements ShouldQueue
{
/*
|--------------------------------------------------------------------------
| Queueable Jobs
|--------------------------------------------------------------------------
|
| This job base class provides a central location to place any logic that
| is shared across all of your jobs. The trait included with the class
| provides access to the "queueOn" and "delay" queue helper methods.
|
*/
use InteractsWithQueue, Queueable, SerializesModels;
}
<?php
namespace App\Listeners;
use App\Events\ExampleEvent;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class ExampleListener
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param \App\Events\ExampleEvent $event
* @return void
*/
public function handle(ExampleEvent $event)
{
//
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Calls extends Model {
protected $table = "calls";
protected $fillable = [ "sid", "number", "dtmf_lang", "dtmf_tracking", "recording_id", "recording_url", "transcript" ];
protected $dateFormat = 'U';
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Staff extends Model {
protected $table = "staff";
protected $fillable = [ "email", "password" ];
protected $hidden = [ "password" ];
protected $dateFormat = 'U';
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Tickets extends Model {
protected $table = "tickets";
protected $fillable = [ "call_id" ];
protected $dateFormat = 'U';
// ASSOCIATIONS
public function call() { return $this->hasOne(\App\Models\Calls::class, "id", "call_id"); }
}
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}
<?php
namespace App\Providers;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Boot the authentication services for the application.
*
* @return void
*/
public function boot()
{
// Here you may define how you wish users to be authenticated for your Lumen
// application. The callback which receives the incoming request instance
// should return either a User instance or null. You're free to obtain
// the User instance via an API token or any other method necessary.
$this->app['auth']->viaRequest('api', function ($request) {
if ($request->input('api_token')) {
return User::where('api_token', $request->input('api_token'))->first();
}
});
}
}
<?php
namespace App\Providers;
use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
\App\Events\ExampleEvent::class => [
\App\Listeners\ExampleListener::class,
],
];
/**
* Determine if events and listeners should be automatically discovered.
*
* @return bool
*/
public function shouldDiscoverEvents()
{
return false;
}
}
package email
import (
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ses"
)
func Send(to string, subject string, text string) error {
aws_credentials := credentials.NewStaticCredentials("AKIATDF35SINNWD4L77P", "TGlqi9rVAVgO3atLN0rbS+f9nxjEMbZfiT8YWEyr", "")
aws_session, err := session.NewSession(&aws.Config{
Region: aws.String("ap-southeast-1"),
Credentials: aws_credentials,
})
if err != nil {
log.Println(err)
}
aws_ses := ses.New(aws_session)
email := &ses.SendEmailInput{
Destination: &ses.Destination{
ToAddresses: []*string{
aws.String(to),
},
},
Message: &ses.Message{
Body: &ses.Body{
Html: &ses.Content{
Charset: aws.String("UTF-8"),
Data: aws.String(text),
},
},
Subject: &ses.Content{
Charset: aws.String("UTF-8"),
Data: aws.String(subject),
},
},
Source: aws.String("noreply@dcsrp.xyz"),
}
_, err = aws_ses.SendEmail(email)
if err != nil {
log.Println(err)
return err
}
return nil
}
#!/usr/bin/env php
<?php
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| First we need to get an application instance. This creates an instance
| of the application / container and bootstraps the application so it
| is ready to receive HTTP / Console requests from the environment.
|
*/
$app = require __DIR__.'/bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/
$kernel = $app->make(
'Illuminate\Contracts\Console\Kernel'
);
exit($kernel->handle(new ArgvInput, new ConsoleOutput));
<?php
require_once __DIR__.'/../vendor/autoload.php';
(new Laravel\Lumen\Bootstrap\LoadEnvironmentVariables(
dirname(__DIR__)
))->bootstrap();
date_default_timezone_set(env('APP_TIMEZONE', 'UTC'));
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| Here we will load the environment and create the application instance
| that serves as the central piece of this framework. We'll use this
| application as an "IoC" container and router for this framework.
|
*/
$app = new Laravel\Lumen\Application(
dirname(__DIR__)
);
// $app->withFacades();
$app->withEloquent();
/*
|--------------------------------------------------------------------------
| Register Container Bindings
|--------------------------------------------------------------------------
|
| Now we will register a few bindings in the service container. We will
| register the exception handler and the console kernel. You may add
| your own bindings here if you like or you can make another file.
|
*/
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
/*
|--------------------------------------------------------------------------
| Register Config Files
|--------------------------------------------------------------------------
|
| Now we will register the "app" configuration file. If the file exists in
| your configuration directory it will be loaded; otherwise, we'll load
| the default version. You may register other files below as needed.
|
*/
$app->configure('app');
/*
|--------------------------------------------------------------------------
| Register Middleware
|--------------------------------------------------------------------------
|
| Next, we will register the middleware with the application. These can
| be global middleware that run before and after each request into a
| route or middleware that'll be assigned to some specific routes.
|
*/
// $app->middleware([
// App\Http\Middleware\ExampleMiddleware::class
// ]);
// $app->routeMiddleware([
// 'auth' => App\Http\Middleware\Authenticate::class,
// ]);
/*
|--------------------------------------------------------------------------
| Register Service Providers
|--------------------------------------------------------------------------
|
| Here we will register all of the application's service providers which
| are used to bind services into the container. Service providers are
| totally optional, so you are not required to uncomment this line.
|
*/
// $app->register(App\Providers\AppServiceProvider::class);
// $app->register(App\Providers\AuthServiceProvider::class);
// $app->register(App\Providers\EventServiceProvider::class);
/*
|--------------------------------------------------------------------------
| Load The Application Routes
|--------------------------------------------------------------------------
|
| Next we will include the routes file so that they can all be added to
| the application. This will provide all of the URLs the application
| can respond to, as well as the controllers that may handle them.
|
*/
$app->router->group([
'namespace' => 'App\Http\Controllers',
], function ($router) {
require __DIR__.'/../routes/web.php';
});
return $app;
{
"name": "laravel/lumen",
"description": "The Laravel Lumen Framework.",
"keywords": ["framework", "laravel", "lumen"],
"license": "MIT",
"type": "project",
"require": {
"php": "^8.1",
"firebase/php-jwt": "^6.5",
"laravel/lumen-framework": "^10.0"
},
"require-dev": {
"fakerphp/faker": "^1.9.1",
"mockery/mockery": "^1.4.4",
"phpunit/phpunit": "^10.0"
},
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
]
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true
},
"minimum-stability": "stable",
"prefer-stable": true
}
This diff is collapsed.
package auth
import (
"dcsrp/models"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/golang-jwt/jwt"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
func Login(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
/**
* Validate if we got email and the password in the
* request and return error if fields are empty.
*/
var Request struct {
Email string `json:"email" binding:"required"`
Password string `json:"password" binding:"required"`
}
if err := c.ShouldBindBodyWith(&Request, binding.JSON); err != nil {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "malformed",
"message": "invalid login credentials",
},
})
return
}
/**
* Query data from the database for the requested email
* address and compare the password hash with the supplied
* password to verify the user and return the JWT upon
* successfull request.
*/
var Staff models.Staff
if err := db.Where("email", Request.Email).First(&Staff).Error; err != nil {
if err == gorm.ErrRecordNotFound {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "invalid_email",
"fields": gin.H{
"email": "invalid email address",
},
},
})
return
} else {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"errors": gin.H{
"ref": "invalid_email",
"fields": gin.H{
"email": "invalid email address",
},
},
})
return
}
}
/**
* Next we compare the password and return error response if
* the password is incorrect.
*/
err := bcrypt.CompareHashAndPassword([]byte(Staff.Password), []byte(Request.Password))
if err != nil {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "invalid_password",
"fields": gin.H{
"password": "incorrect password",
},
},
})
return
}
/**
* Seems like everything was correct and it's time to generate
* the JWT token for the user with the relevent information
* in it and pass it in the response as the token.
*/
claims := jwt.MapClaims{}
claims["iss"] = "dcsrp"
claims["sub"] = Staff.ID
claims["email"] = Staff.Email
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
jwt, err := token.SignedString([]byte("cb4cf82d320a462db851a5fccd06366b"))
if err != nil {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "jwt_error",
"fields": gin.H{
"email": "token generating error",
},
},
})
return
}
/*
* Seems everything was fine and we have the JWT token upon
* successfull authentication process.
*/
c.AbortWithStatusJSON(200, gin.H{
"status": "success",
"token": jwt,
})
}
package customer
import (
"dcsrp/models"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/golang-jwt/jwt"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
func Login(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
/**
* Validate if we got email and the password in the
* request and return error if fields are empty.
*/
var Request struct {
Email string `json:"email" binding:"required"`
Password string `json:"password" binding:"required"`
}
if err := c.ShouldBindBodyWith(&Request, binding.JSON); err != nil {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "malformed",
"message": "invalid login credentials",
},
})
return
}
/**
* Query data from the database for the requested email
* address and compare the password hash with the supplied
* password to verify the user and return the JWT upon
* successfull request.
*/
var Customer models.Customers
if err := db.Where("email", Request.Email).First(&Customer).Error; err != nil {
if err == gorm.ErrRecordNotFound {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "invalid_email",
"fields": gin.H{
"email": "invalid email address",
},
},
})
return
} else {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"errors": gin.H{
"ref": "invalid_email",
"fields": gin.H{
"email": "invalid email address",
},
},
})
return
}
}
/**
* Next we compare the password and return error response if
* the password is incorrect.
*/
err := bcrypt.CompareHashAndPassword([]byte(Customer.Password), []byte(Request.Password))
if err != nil {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "invalid_password",
"fields": gin.H{
"password": "incorrect password",
},
},
})
return
}
/**
* Seems like everything was correct and it's time to generate
* the JWT token for the user with the relevent information
* in it and pass it in the response as the token.
*/
claims := jwt.MapClaims{}
claims["iss"] = "dcsrp"
claims["sub"] = Customer.ID
claims["email"] = Customer.Email
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
jwt, err := token.SignedString([]byte("cb4cf82d320a462db851a5fccd06366b"))
if err != nil {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "jwt_error",
"fields": gin.H{
"email": "token generating error",
},
},
})
return
}
/*
* Seems everything was fine and we have the JWT token upon
* successfull authentication process.
*/
c.AbortWithStatusJSON(200, gin.H{
"status": "success",
"token": jwt,
"customer": gin.H{
"id": Customer.ID,
},
})
}
package ticket
import (
"dcsrp/app/email"
"dcsrp/models"
"fmt"
"log"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"gorm.io/gorm"
)
func Agent(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
/**
* We need to bind JSON data values into struct
* before start processing them with the application
* logic.
*/
var Request struct {
TicketID uint64 `json:"ticket" binding:"required"`
}
if err := c.ShouldBindBodyWith(&Request, binding.JSON); err != nil {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "malformed",
"message": "malformed",
},
})
return
}
/**
* Identify the relevent ticket for the specific
* function call and return the ticket model to
* the next controller to process
*/
var Ticket models.Tickets
db.Where("id", Request.TicketID).First(&Ticket)
/**
* Update ticket to the manual mode and dispatch email
* to customer with updated status
*/
db.Model(&Ticket).Updates(models.Tickets{Agent: "manual"})
/**
* Query customer information associated with the ticket
* to generate the email message
*/
var Customer models.Customers
db.Where("id", Ticket.CustomerID).First(&Customer)
text := fmt.Sprintf("Hi %s<br><br>Your ticket <strong>#%d</strong> status got updated to <strong>manual mode</strong>. Please wait for response from our customer support department.<br><br>Thank you.", Customer.Name, Ticket.ID)
if err := email.Send(Customer.Email, fmt.Sprintf("[UPDATE] Ticket #%d", Ticket.ID), text); err != nil {
log.Println(err)
}
// RESPONSE
c.AbortWithStatusJSON(200, gin.H{
"status": "success",
})
}
package ticket
import (
"dcsrp/app/email"
"dcsrp/models"
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Assign(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
/**
* Get information about the ticket we are trying to
* assign before do any future work.
*/
var Ticket models.Tickets
db.Preload("Customer").Where("id", c.Param("id")).First(&Ticket)
/**
* Send an email to customer saying that this ticket
* got assigned to the staff member with the details
* of that spoecific staff memeber.
*/
text := fmt.Sprintf("Hi %s<br><br>Your ticket <strong>#%d</strong> got assigned to <strong>Dilakshi Lamahewa</strong>. Please wait for response from our customer support department.<br><br>Thank you.", Ticket.Customer.Name, Ticket.ID)
go email.Send(Ticket.Customer.Email, fmt.Sprintf("[UPDATE] Ticket #%d", Ticket.ID), text)
/**
* Send another email to staff member to ensure that
* he/she is aware of this assignment
*/
go email.Send("dilakshilamahewa@gmail.com", fmt.Sprintf("[UPDATE] Ticket #%d", Ticket.ID), fmt.Sprintf("Hi Dilakshi,<br><br>Ticket #%d is now assigned to you.", Ticket.ID))
/**
* Update ticket to manual mode to reflect that
* the ticket is now handled by human
*/
db.Model(&models.Tickets{}).Where("id", Ticket.ID).Update("agent", "manual")
/**
* Finally update the ticket timeline to reflect the
* newly assigned customer support agent
*/
db.Create(&models.TicketTimeline{
TicketID: Ticket.ID,
Description: "Ticket is assigned to staff member Dilakshi Lamahewa",
})
c.AbortWithStatusJSON(200, gin.H{
"status": "success",
})
}
package ticket
import (
"dcsrp/models"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Get(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
var Ticket models.Tickets
db.Preload("Call").Preload("Customer").Preload("Timeline").Where("id", c.Param("id")).First(&Ticket)
var timeline []map[string]interface{}
/**
* Append predefined timeline items to ticket before
* append any dynamically generated items
*/
timeline = append(timeline, map[string]interface{}{
"id": 0,
"description": "Ticket created",
"time": map[string]int64{
"created": Ticket.CreatedAt,
"updated": Ticket.UpdatedAt,
},
})
// LOOP
for _, item := range Ticket.Timeline {
timeline = append(timeline, map[string]interface{}{
"id": item.ID,
"description": item.Description,
"time": map[string]int64{
"created": item.CreatedAt,
"updated": item.UpdatedAt,
},
})
}
ticket := map[string]interface{}{
"id": Ticket.ID,
"status": Ticket.Status,
"agent": Ticket.Agent,
"call": map[string]interface{}{
"id": Ticket.Call.ID,
"transcript": Ticket.Call.Transcript,
},
"customer": map[string]interface{}{},
"timeline": timeline,
"time": map[string]int64{
"created": Ticket.CreatedAt,
"updated": Ticket.UpdatedAt,
},
}
c.AbortWithStatusJSON(200, gin.H{
"status": "success",
"data": gin.H{
"ticket": ticket,
},
})
}
package ticket
import (
"dcsrp/models"
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func List(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
/**
* We have to check if the pagination values are configured in the
* original request or not. We can go with the default if nothing
* present in the request.
*/
var Request struct {
Page int `form:"page" binding:"numeric"`
Limit int `form:"limit" binding:"numeric"`
CustomerID int `form:"customer" binding:"numeric"`
}
if err := c.Bind(&Request); err != nil {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "malformed",
},
})
return
}
/**
* We have to check if the request have defined the page and
* limit value for return message history data or otherwise
* we have to set it to the default record limits
*/
if Request.Page == 0 {
Request.Page = 1
}
if Request.Limit == 0 {
Request.Limit = 10
}
var tickets []models.Tickets
if Request.CustomerID == 0 {
db.Offset(int(Request.Page-1) * Request.Limit).Limit(Request.Limit).Order("id desc").Find(&tickets)
} else {
db.Offset(int(Request.Page-1)*Request.Limit).Limit(Request.Limit).Where("customer_id", Request.CustomerID).Order("id desc").Find(&tickets)
}
/**
* Get call information related to the ticket we are
* currently handling
*/
var list []map[string]interface{}
for _, ticket := range tickets {
var Call models.Calls
var Customer models.Customers
var Timeline []models.TicketTimeline
db.Where("id", ticket.CallID).First(&Call)
db.Where("id", ticket.CustomerID).First(&Customer)
db.Where("ticket_id", ticket.ID).Find(&Timeline)
list = append(list, map[string]interface{}{
"id": ticket.ID,
"title": fmt.Sprintf("Ticket %d On %s", ticket.ID, "09/07/2023"),
"status": ticket.Status,
"agent": ticket.Agent,
"call": map[string]interface{}{
"number": Call.Number,
"transcript": Call.Transcript,
},
"customer": map[string]interface{}{
"id": Customer.ID,
"email": Customer.Email,
"name": Customer.Name,
"type": Customer.Type,
"mobile": Customer.Mobile,
"province": Customer.Province,
},
"timeline": Timeline,
"time": map[string]int64{
"created": ticket.CreatedAt,
"updated": ticket.UpdatedAt,
},
})
}
c.AbortWithStatusJSON(200, gin.H{
"status": "success",
"data": gin.H{
"tickets": list,
},
})
}
func ListManual(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
/**
* We have to check if the pagination values are configured in the
* original request or not. We can go with the default if nothing
* present in the request.
*/
var Request struct {
Page int `form:"page" binding:"numeric"`
Limit int `form:"limit" binding:"numeric"`
}
if err := c.Bind(&Request); err != nil {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "malformed",
},
})
return
}
/**
* We have to check if the request have defined the page and
* limit value for return message history data or otherwise
* we have to set it to the default record limits
*/
if Request.Page == 0 {
Request.Page = 1
}
if Request.Limit == 0 {
Request.Limit = 10
}
var tickets []models.Tickets
db.Where("agent", "manual").Offset(int(Request.Page-1) * Request.Limit).Limit(Request.Limit).Order("id desc").Find(&tickets)
/**
* Get call information related to the ticket we are
* currently handling
*/
var list []map[string]interface{}
for _, ticket := range tickets {
var Call models.Calls
var Customer models.Customers
var Timeline []models.TicketTimeline
db.Where("id", ticket.CallID).First(&Call)
db.Where("id", ticket.CustomerID).First(&Customer)
db.Where("ticket_id", ticket.ID).Find(&Timeline)
list = append(list, map[string]interface{}{
"id": ticket.ID,
"title": fmt.Sprintf("Ticket %d On %s", ticket.ID, "09/07/2023"),
"status": ticket.Status,
"agent": ticket.Agent,
"call": map[string]interface{}{
"number": Call.Number,
"transcript": Call.Transcript,
},
"customer": map[string]interface{}{
"id": Customer.ID,
"email": Customer.Email,
"name": Customer.Name,
"type": Customer.Type,
"mobile": Customer.Mobile,
"province": Customer.Province,
},
"timeline": Timeline,
"time": map[string]int64{
"created": ticket.CreatedAt,
"updated": ticket.UpdatedAt,
},
})
}
c.AbortWithStatusJSON(200, gin.H{
"status": "success",
"data": gin.H{
"tickets": list,
},
})
}
package ticket
import (
"dcsrp/app/email"
"dcsrp/models"
"fmt"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"gorm.io/gorm"
)
func Message(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
var Request struct {
Message string `json:"message" binding:"required"`
}
if err := c.ShouldBindBodyWith(&Request, binding.JSON); err != nil {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "malformed",
"message": "malformed",
},
})
return
}
/**
* Query the information regarding the ticket we are
* handling because we need to send notifications
* out.
*/
var Ticket models.Tickets
db.Preload("Customer").Where("id", c.Param("id")).First(&Ticket)
/**
* Send an email to customer with the new message
* added by the customer support agent so customer
* can be informed
*/
text := fmt.Sprintf("Hi %s<br><br>Your ticket <strong>#%d</strong> have new response from customer support agent <strong>Dilakshi Lamahewa</strong><br><br><strong>Message: %s</strong>.<br><br>Thank you.", Ticket.Customer.Name, Ticket.ID, Request.Message)
email.Send(Ticket.Customer.Email, fmt.Sprintf("[UPDATE] Ticket #%d", Ticket.ID), text)
/**
* Update timeline of the ticket regarding the newly
* updated content
*/
db.Create(&models.TicketTimeline{
TicketID: Ticket.ID,
Description: fmt.Sprintf("Message: %s", Request.Message),
})
c.AbortWithStatusJSON(200, gin.H{
"status": "success",
})
fmt.Println(Request)
fmt.Println(Ticket)
}
package ticket
import (
"dcsrp/models"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"gorm.io/gorm"
)
func Post(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
/**
* Validate if we got correct information in the
* request and return error if fields are empty.
*/
var Request struct {
TicketID uint64 `json:"ticket" binding:"required"`
Description string `json:"description" binding:"required"`
}
if err := c.ShouldBindBodyWith(&Request, binding.JSON); err != nil {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "malformed",
"message": "malformed",
},
})
return
}
/**
* Update ticket timeline with the new information
* we received
*/
db.Create(&models.TicketTimeline{
TicketID: Request.TicketID,
Description: Request.Description,
})
}
package user
import (
"dcsrp/models"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Roles(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
Staff := c.MustGet("staff").(models.Staff)
var Roles []models.StaffRoles
db.Where("staff_id", Staff.ID).Find(&Roles)
var roles []string
for _, role := range Roles {
roles = append(roles, role.Role)
}
c.AbortWithStatusJSON(200, gin.H{
"status": "success",
"data": gin.H{
"roles": roles,
},
})
}
<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class UserFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = User::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'name' => $this->faker->name,
'email' => $this->faker->unique()->safeEmail,
];
}
}
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// $this->call('UsersTableSeeder');
}
}
package main
import (
middleware "dcsrp/middlewares"
"github.com/gin-gonic/gin"
)
func main() {
/**
* We have to define the operation for go/gin router
* to work. By default we'll use the debug mode and
* this will changed to release mode on the production
* environments.
*/
gin.SetMode(gin.DebugMode)
/**
* Here we'll create go/gin Router handler to take care about
* the routing inside in the application. There will be no
* middlewares in the init stage.
*/
Router := gin.New()
/**
* Logger middleware will write the logs to gin.DefaultWriter
* We are supposed to use some custom made Logger to make log
* rotate. But this will be here for while
*/
Router.Use(gin.Logger())
/**
* We have to make our application immune to unexpected 500
* server side errors. This Recovery middleware will recovers
* from any panics and writes a 500 if there was one.
*/
Router.Use(gin.Recovery())
/**
* We need to return some CORS headers to fix issues with
* cross browser requests things.
*/
Router.Use(middleware.CORS())
/**
* Initialize gorm ORM instance and inject it to the main
* gin application to use in the controllers.
*/
Router.Use(middleware.Database())
/**
* This is where we include all the routes define in our application
* into the go-gin router
*/
Routes(Router)
/**
* It's time to start the go/gin Router and make it available
* to our front facing application to make requests.
*/
Router.Run("127.0.0.1:8282")
}
package functions
import (
"dcsrp/app/email"
"dcsrp/models"
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func WorkingHours(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
Ticket := c.MustGet("ticket").(models.Tickets)
/**
* Customer is asking regarding the working hours
* of the company so we can return the working
* hours using email
*/
body := fmt.Sprintf("Hello %s,<br><br>As per your recent inquiry below are the working hours of our company.<br><br><strong>Monday:</strong> 09:00 AM - 05:00 PM<br><strong>Tuesday:</strong> 09:00 AM - 05:00 PM<br><strong>Wednesday:</strong> 09:00 AM - 05:00 PM<br><strong>Thursday:</strong> 09:00 AM - 05:00 PM<br><strong>Friday:</strong> 09:00 AM - 05:00 PM<br><strong>Saturday:</strong> 09:00 AM - 01:00 PM<br><strong>Sunday:</strong> 09:00 AM - 01:00 PM<br><br>Thank you.", Ticket.Customer.Name)
email.Send(Ticket.Customer.Email, fmt.Sprintf("[UPDATE] Ticket #%d", Ticket.ID), body)
/**
* Update timeline regarding the recent action
* that triggered using the function
*/
db.Create(&models.TicketTimeline{
TicketID: Ticket.ID,
Description: "AI: Company working hours sent to customer",
})
// ABORT
c.AbortWithStatusJSON(200, gin.H{"status": "success"})
}
func ContactInformation(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
Ticket := c.MustGet("ticket").(models.Tickets)
/**
* Customer is asking regarding the contact info
* of the company.
*/
body := fmt.Sprintf("Hello %s,<br><br>As per your recent inquiry below are the contact information of our company.<br><br><strong>Email:</strong> info@dcsrp.xyz<br><strong>Hotline:</strong> +18449700034<br><br>Thank you.", Ticket.Customer.Name)
email.Send(Ticket.Customer.Email, fmt.Sprintf("[UPDATE] Ticket #%d", Ticket.ID), body)
/**
* Update timeline regarding the recent action
* that triggered using the function
*/
db.Create(&models.TicketTimeline{
TicketID: Ticket.ID,
Description: "AI: Company contact information sent to customer",
})
// ABORT
c.AbortWithStatusJSON(200, gin.H{"status": "success"})
}
package functions
import (
"dcsrp/app/email"
"dcsrp/models"
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func Track(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
Ticket := c.MustGet("ticket").(models.Tickets)
/**
* Get currrent tracking information for the customer
* package using the tracking number he/she entered
*/
var Packages models.Packages
var PackageInfo models.PackageInfo
db.Where("package_no", Ticket.Call.DTMFTracking).First(&Packages)
db.Where("package_id", Packages.ID).First(&PackageInfo)
/**
* Send out the tracking information to the customer
* by quering package information for the specific
* tracking number
*/
body := fmt.Sprintf("Hello %s,<br><br>Below are the tracking information for your recent inquiry.<br><br><strong>Tracking:</strong> %d<br><br><strong>Location:</strong> %s<br><strong>Time:</strong> %s<br><br>Thank you.", Ticket.Customer.Name, Packages.Number, PackageInfo.Location, Packages.Date)
email.Send(Ticket.Customer.Email, fmt.Sprintf("[UPDATE] Ticket #%d", Ticket.ID), body)
/**
* Update timeline regarding the recent action
* that triggered using the function
*/
db.Create(&models.TicketTimeline{
TicketID: Ticket.ID,
Description: "AI: Package tracking information sent to customer",
})
// ABORT
c.AbortWithStatusJSON(200, gin.H{"status": "success"})
}
func IsDefected(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
Ticket := c.MustGet("ticket").(models.Tickets)
/**
* Get currrent tracking information for the customer
* package using the tracking number he/she entered
*/
var Packages models.Packages
db.Where("package_no", Ticket.Call.DTMFTracking).First(&Packages)
// EMAIL
body := fmt.Sprintf("Hello %s,<br><br>Below are the defect status information for your recent inquiry.<br><br><strong>Tracking:</strong> %d<br><br><strong>Status:</strong> %s<br><strong>Time:</strong> %s<br><br>Thank you.", Ticket.Customer.Name, Packages.Number, Packages.InquryDescription, Packages.Date)
email.Send(Ticket.Customer.Email, fmt.Sprintf("[UPDATE] Ticket #%d", Ticket.ID), body)
/**
* Update timeline regarding the recent action
* that triggered using the function
*/
db.Create(&models.TicketTimeline{
TicketID: Ticket.ID,
Description: "AI: Package defect status send to customer",
})
// ABORT
c.AbortWithStatusJSON(200, gin.H{"status": "success"})
}
module dcsrp
go 1.20
require (
github.com/aws/aws-sdk-go v1.44.327
github.com/gin-gonic/gin v1.9.1
github.com/golang-jwt/jwt v3.2.2+incompatible
golang.org/x/crypto v0.9.0
gorm.io/driver/mysql v1.5.1
gorm.io/gorm v1.25.2
gorm.io/plugin/soft_delete v1.2.1
)
require (
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
This diff is collapsed.
package middleware
import (
"dcsrp/models"
"strings"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
"gorm.io/gorm"
)
func Auth() gin.HandlerFunc {
return func(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
/*
* We have to validate the request authorization header
* is supplied and it's valid before continue the request
*/
token := c.GetHeader("authorization")
if len(token) == 0 {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "invalid_token",
},
})
return
}
/*
* We have the token supplied in the request authorization
* header and we have to split token to extract the jwt from
* the header.
*/
if len(strings.Split(token, " ")) != 2 {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "invalid_token",
},
})
return
}
/*
* We have the token and we need to decode and get the token id
* and then we can verify the token validity using the hmac
* secret stored in the database.
*/
claims := jwt.MapClaims{}
_, err := jwt.ParseWithClaims(strings.Split(token, " ")[1], &claims, func(token *jwt.Token) (interface{}, error) {
return []byte("cb4cf82d320a462db851a5fccd06366b"), nil
})
if err != nil {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "access_denied",
},
})
return
}
/**
* Check if the user account is valid and return uint64 type
* user id to the next middleware.
*/
var Staff models.Staff
if rows_affected := db.First(&Staff, uint64(claims["sub"].(float64))).RowsAffected; rows_affected <= 0 {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "invalid_user_account",
},
})
return
}
/*
* We have to pass user id from the token claims to the
* next middleware of the application to identify the user
* and continue the task.
*/
c.Set("staff", Staff)
c.Next()
}
}
package middleware
import (
"github.com/gin-gonic/gin"
)
func CORS() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(200)
} else {
c.Next()
}
}
}
package middleware
import (
"time"
"github.com/gin-gonic/gin"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
func Database() gin.HandlerFunc {
return func(c *gin.Context) {
db, _ := gorm.Open(mysql.New(mysql.Config{
DSN: "dcsrp_db:BJEutn4UT5z9Vfye6LWe7kXK@tcp(dcsrp-db-01.cvqssef8vzgx.ap-southeast-1.rds.amazonaws.com:3306)/dcsrp_db?charset=utf8&parseTime=True&loc=Local",
}), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
/**
* Configure SQL connection pool settings to avoid any
* funky things from happening with connection limites
*/
connection, _ := db.DB()
connection.SetConnMaxLifetime(time.Second)
c.Set("db", db)
c.Next()
}
}
package middleware
import (
"dcsrp/models"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"gorm.io/gorm"
)
func Ticket() gin.HandlerFunc {
return func(c *gin.Context) {
db := c.MustGet("db").(*gorm.DB)
/**
* We need to bind JSON data values into struct
* before start processing them with the application
* logic.
*/
var Request struct {
TicketID uint64 `json:"ticket" binding:"required"`
}
if err := c.ShouldBindBodyWith(&Request, binding.JSON); err != nil {
c.AbortWithStatusJSON(400, gin.H{
"status": "failed",
"error": gin.H{
"ref": "malformed",
"message": "malformed",
},
})
return
}
/**
* Identify the relevent ticket for the specific
* function call and return the ticket model to
* the next controller to process
*/
var Ticket models.Tickets
db.Preload("Customer").Preload("Call").Where("id", Request.TicketID).First(&Ticket)
c.Set("ticket", Ticket)
c.Next()
}
}
package models
import (
"gorm.io/plugin/soft_delete"
)
type Calls struct {
ID uint64 `gorm:"column:id;primaryKey"`
SID string `gorm:"column:sid"`
Number uint64 `gorm:"column:number"`
DTMFLang int64 `gorm:"column:dtmf_lang"`
DTMFTracking int64 `gorm:"column:dtmf_tracking"`
RecordingID string `gorm:"column:recording_id"`
RecordingURL string `gorm:"column:recording_url"`
Transcript string `gorm:"column:transcript"`
CreatedAt int64 `gorm:"column:created_at;autoCreateTime"`
UpdatedAt int64 `gorm:"column:updated_at;autoUpdateTime"`
DeletedAt soft_delete.DeletedAt `gorm:"column:deleted_at;default:null"`
}
func (Calls) TableName() string {
return "calls"
}
package models
type Customers struct {
ID uint64 `gorm:"column:id;primaryKey"`
Email string `gorm:"column:email"`
Password string `gorm:"column:password"`
Name string `gorm:"column:name"`
Type string `gorm:"column:type"`
Mobile uint64 `gorm:"column:mobile"`
NoOfPices uint64 `gorm:"column:no_of_pices"`
Province string `gorm:"column:province"`
Date string `gorm:"column:date"`
}
func (Customers) TableName() string {
return "customers"
}
package models
type PackageInfo struct {
ID uint64 `gorm:"column:id;primaryKey"`
PackageID uint64 `gorm:"column:package_id"`
Location string `gorm:"column:location"`
}
func (PackageInfo) TableName() string {
return "package_info"
}
package models
type Packages struct {
ID uint64 `gorm:"column:id;primaryKey"`
CustomerID uint64 `gorm:"column:customer_id"`
RecepientID uint64 `gorm:"column:recepient_id"`
Type string `gorm:"column:type"`
PredeterminedDays string `gorm:"column:predetermined_days"`
Date string `gorm:"column:date"`
Number uint64 `gorm:"column:package_no"`
SenderName string `gorm:"column:sender_name"`
ReceiverName string `gorm:"column:receiver_name"`
Description string `gorm:"column:package_description"`
InquryDescription string `gorm:"column:inqury_package_description"`
}
func (Packages) TableName() string {
return "packages"
}
package models
import (
"gorm.io/plugin/soft_delete"
)
type Staff struct {
ID uint64 `gorm:"column:id;primaryKey"`
Email string `gorm:"column:email"`
Password string `gorm:"column:password"`
CreatedAt int64 `gorm:"column:created_at;autoCreateTime"`
UpdatedAt int64 `gorm:"column:updated_at;autoUpdateTime"`
DeletedAt soft_delete.DeletedAt `gorm:"column:deleted_at;default:null"`
Roles []StaffRoles `gorm:"references:ID;foreignKey:StaffID"`
}
func (Staff) TableName() string {
return "staff"
}
package models
import (
"gorm.io/plugin/soft_delete"
)
type StaffRoles struct {
ID uint64 `gorm:"column:id;primaryKey"`
StaffID uint64 `gorm:"column:staff_id"`
Role string `gorm:"column:role"`
CreatedAt int64 `gorm:"column:created_at;autoCreateTime"`
UpdatedAt int64 `gorm:"column:updated_at;autoUpdateTime"`
DeletedAt soft_delete.DeletedAt `gorm:"column:deleted_at;default:null"`
}
func (StaffRoles) TableName() string {
return "staff_roles"
}
package models
import (
"gorm.io/plugin/soft_delete"
)
type TicketTimeline struct {
ID uint64 `gorm:"column:id;primaryKey"`
TicketID uint64 `gorm:"column:ticket_id"`
Description string `gorm:"column:description"`
CreatedAt int64 `gorm:"column:created_at;autoCreateTime"`
UpdatedAt int64 `gorm:"column:updated_at;autoUpdateTime"`
DeletedAt soft_delete.DeletedAt `gorm:"column:deleted_at;default:null"`
}
func (TicketTimeline) TableName() string {
return "ticket_timeline"
}
package models
import (
"gorm.io/plugin/soft_delete"
)
type Tickets struct {
ID uint64 `gorm:"column:id;primaryKey"`
CallID uint64 `gorm:"column:call_id"`
CustomerID uint64 `gorm:"column:customer_id"`
Status string `gorm:"column:status"`
Agent string `gorm:"column:agent"`
CreatedAt int64 `gorm:"column:created_at;autoCreateTime"`
UpdatedAt int64 `gorm:"column:updated_at;autoUpdateTime"`
DeletedAt soft_delete.DeletedAt `gorm:"column:deleted_at;default:null"`
Call Calls `gorm:"references:CallID;foreignKey:ID"`
Customer Customers `gorm:"references:CustomerID;foreignKey:ID"`
Timeline []TicketTimeline `gorm:"references:ID;foreignKey:TicketID"`
}
func (Tickets) TableName() string {
return "tickets"
}
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
>
<testsuites>
<testsuite name="Application Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
</php>
</phpunit>
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
<?php
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| First we need to get an application instance. This creates an instance
| of the application / container and bootstraps the application so it
| is ready to receive HTTP / Console requests from the environment.
|
*/
$app = require __DIR__.'/../bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/
$app->run();
package main
import (
"github.com/gin-gonic/gin"
Auth_Controller "dcsrp/controllers/auth"
Customer_Controller "dcsrp/controllers/customer"
Ticket_Controller "dcsrp/controllers/ticket"
User_Controller "dcsrp/controllers/user"
Functions_Controller "dcsrp/functions"
middleware "dcsrp/middlewares"
)
func Routes(Router *gin.Engine) {
Routes_v1 := Router.Group("/v1.0")
{
// AUTH & USER
Routes_v1.POST("/auth/login", Auth_Controller.Login)
Routes_v1.GET("/user/roles", middleware.Auth(), User_Controller.Roles)
// TICKETS
Routes_v1.GET("/ticket/:id", Ticket_Controller.Get)
Routes_v1.POST("/ticket/:id/assign", Ticket_Controller.Assign)
Routes_v1.POST("/ticket/:id/message", Ticket_Controller.Message)
Routes_v1.GET("/tickets", Ticket_Controller.List)
Routes_v1.POST("/ticket/agent", Ticket_Controller.Agent)
Routes_v1.POST("/ticket/timeline", Ticket_Controller.Post)
Routes_v1.GET("/tickets/manual", Ticket_Controller.ListManual)
// CUSTOMER
Routes_v1.POST("/customer/login", Customer_Controller.Login)
// FUNCTIONS
Route_Functions := Routes_v1.Group("/function")
{
Route_Functions.POST("/track_package", middleware.Ticket(), Functions_Controller.Track)
Route_Functions.POST("/defect_status", middleware.Ticket(), Functions_Controller.IsDefected)
Route_Functions.POST("/working_hours", middleware.Ticket(), Functions_Controller.WorkingHours)
Route_Functions.POST("/contact_information", middleware.Ticket(), Functions_Controller.ContactInformation)
}
}
/**
* We have to define ping route to get health information about
* the Zend API to intergrate with monitoring utilities.
*/
Router.GET("/ping", func(c *gin.Context) {
c.AbortWithStatusJSON(200, gin.H{
"status": "success",
})
})
/*
* We have to show resource not found error if some
* application request undefined route.
*/
Router.NoRoute(func(c *gin.Context) {
c.AbortWithStatusJSON(404, gin.H{
"status": "failed",
"error": gin.H{
"ref": "not_found",
"message": "Resource not found",
},
})
})
}
<?php
/** @var \Laravel\Lumen\Routing\Router $router */
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It is a breeze. Simply tell Lumen the URIs it should respond to
| and give it the Closure to call when that URI is requested.
|
*/
$router->get("/", "HomeController@home");
// AUTH
$router->post("/auth/login", "AuthController@login");
// TICKETS
$router->get("/tickets", "TicketController@get");
$router->post("/callback/ticket", "TicketController@callback_post");
<?php
namespace Tests;
use Laravel\Lumen\Testing\DatabaseMigrations;
use Laravel\Lumen\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function test_that_base_endpoint_returns_a_successful_response()
{
$this->get('/');
$this->assertEquals(
$this->app->version(), $this->response->getContent()
);
}
}
<?php
namespace Tests;
use Laravel\Lumen\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
/**
* Creates the application.
*
* @return \Laravel\Lumen\Application
*/
public function createApplication()
{
return require __DIR__.'/../bootstrap/app.php';
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment