TxdDocker.psm1

if (-not (Test-Path env:TXD_DOCKER_ROOT)) { 
      if($isLinux){
            $env:TXD_DOCKER_ROOT = '/docker/laravel_docker'
      }else{
            $env:TXD_DOCKER_ROOT = 'C:\docker\laravel_docker' 
      }
}

if (-not (Test-Path env:TXD_CONTAINER_NAME)) { 
      $env:TXD_CONTAINER_NAME = 'php-dev'
}
if (-not (Test-Path env:TXD_HOST_FOLDER_PATH)) { 
      $env:TXD_HOST_FOLDER_PATH = '\\wsl$\docker-desktop-data\version-pack-data\community\docker\volumes\laravel_docker_sites\_data'
}

function Read-YesNo{
      param($question,
            [switch]$defaultToYes)
      $sample = " (y/N)"
      if($defaultToYes){
            $sample =" (Y/n)"
      }
      do { $answer = Read-Host ($question+$sample) } 
      until ("y","n","Y","N","" -ccontains $answer)
      if("y","Y" -ccontains $answer){
            return $true
      }
      if($defaultToYes -and ($answer -eq "")){
            return $true
      }
      return $false
  }
function Get-DockerEnv{
      $env = ($env:TXD_DOCKER_ROOT+'\.env')
      $envExists = Test-Path ($env) -PathType Leaf
      if(!$envExists){
            Write-Error ".env file not found!";
      }
      $ini = [PSCustomObject]@{};
      foreach($line in Get-Content $env) {
            $withoutComments = $line.Split("#")
            $parts = $withoutComments[0].Split("=")
            if($parts.Count -gt 1){
                  Add-member -InputObject $ini -NotePropertyName $parts[0] -NotePropertyValue ($parts[1] -replace "\\", "/")
            }
      }
      if(-not (Get-Member -Name 'WEBSITE_DOMAIN' -InputObject $ini)){
            Add-member -InputObject $ini -NotePropertyName "WEBSITE_DOMAIN" -NotePropertyValue "dev.jsmservice.eu"      
      }

      if(-not (Get-Member -Name 'COMPOSER_CACHE' -InputObject $ini)){
            Add-member -InputObject $ini -NotePropertyName "COMPOSER_CACHE" -NotePropertyValue ([System.IO.Path]::GetFullPath((Join-Path $env:TXD_DOCKER_ROOT "../.composer_cache")))     
      }

      Add-member -InputObject $ini -NotePropertyName "vhostsPath" -NotePropertyValue (Join-Path $env:TXD_DOCKER_ROOT "nginx/vhosts")
      Add-member -InputObject $ini -NotePropertyName "templatePath" -NotePropertyValue (Join-Path $env:TXD_DOCKER_ROOT "nginx")
      Add-member -InputObject $ini -NotePropertyName "composeFile" -NotePropertyValue (Join-Path $env:TXD_DOCKER_ROOT "docker-compose.yml")
      return $ini
}

function Get-DockerWebsiteName{
      param([switch]$noDefault)
      $dockerEnv = Get-DockerEnv
      $list = Join-Path $dockerEnv.vhostsPath "*"
            | Get-ChildItem -Include *.conf
            | Foreach-object { ([regex]::matches( $_,"[\w\.\-_]+(?=.conf)")).Value}
      if($noDefault){
            $list = $list | Where-Object { $_ –ne "default" }
      }
      $list
}

function Update-DockerWebsiteList{
      $dockerEnv = Get-DockerEnv
      Join-Path $dockerEnv.vhostsPath "*"
            | Get-ChildItem -Include *.conf
            | Foreach-object { ([regex]::matches((Get-Content $_),"(?<=server_name )[\w\.\-_]+")).Value}
            | Sort-Object
            | Set-Content (Join-Path $dockerEnv.WEBSITE_LOCAL_PATH "site_list.txt")
      Get-Content (Join-Path $dockerEnv.WEBSITE_LOCAL_PATH "site_list.txt")
}
function Restart-DockerWebserver{
      param($containerName="")
      $dockerEnv = Get-DockerEnv
      Push-Location $env:TXD_DOCKER_ROOT
      docker-compose -f $dockerEnv.composeFile restart $containerName
      Pop-Location
}
function Remove-DockerWebsite{
      param($name)
      $dockerEnv = Get-DockerEnv
      $fullpath = (Join-Path $dockerEnv.vhostsPath "$name.conf")
      if($name -eq "default"){
            Write-Error "Attenzione! non puoi rimuovere il sito di default"
            return
      }
      
      if(Test-Path $fullpath -PathType Leaf){
            Remove-Item $fullpath
      }else{
            Write-Warning "$fullpath non trovato"
      }
      Update-DockerWebsiteList | Out-Null
}

function get-DockerArtisan {
      
      param(
            $version=73,
            [Parameter(ValueFromRemainingArguments = $true, Position=1)]
            $command
      )
      $folderPath = (get-location).Path
      $dockerEnv = Get-DockerEnv
      $folderPath = $folderPath -replace "\\", "/"
      $folderPath = $folderPath -replace $dockerEnv.WEBSITE_LOCAL_PATH,"" -replace "^/",""
      $phpName = "php73"
      
      if($version -eq 71){
            $phpName ="php71"
      }
      
      Push-location $env:TXD_DOCKER_ROOT

      docker-compose run --rm $phpName php $folderPath/artisan $command

      Pop-location
}

# function get-DockerComposer {
# param($version="latest",
# [Parameter(ValueFromRemainingArguments = $true, Position=1)]
# $command)
# $dockerEnv = Get-DockerEnv
# docker run --rm --interactive --tty --volume ${PWD}:/app --volume ${dockerEnv.COMPOSER_CACHE}:/tmp composer:${version} install
# }

function Add-DockerWebsite{
      param($folderPath=(get-location).Path)
      $dockerEnv = Get-DockerEnv
      $hostname = $dockerEnv.PC_NAME
      $domain = $dockerEnv.WEBSITE_DOMAIN
      $laravelSite = Read-YesNo "Il sito è un progetto Laravel?" -defaultToYes
      $folderPath = $folderPath -replace "\\", "/"
      $oldFolderPath = $folderPath
      $folderPath = $folderPath -replace $dockerEnv.WEBSITE_LOCAL_PATH,"" -replace "^/",""
      $confirmFolder = $false
      if($folderPath -ne $oldFolderPath){
            $fullPath = Join-Path $dockerEnv.WEBSITE_LOCAL_PATH $folderPath
            $confirmFolder = Read-YesNo "Vuoi registrare il percorso corrente ($fullPath)?" -defaultToYes
      }
      if(!$confirmFolder){
            do {
                  $folderPath = Read-Host ("Inserisci il percorso 'relativo' del sito da aggiungere, a partire dalla root riportata sopra (per i sistemi laravel e' la cartella principale SOPRA a public, per gli altri il punto di acceso diretto)`n"+$dockerEnv.WEBSITE_LOCAL_PATH) #`
            }
            until (Test-Path (Join-Path $dockerEnv.WEBSITE_LOCAL_PATH $folderPath) -PathType Container)
      }
      do {
            Write-Output "seleziona la versione di php:"
            Write-Output "1) php-7.1"
            Write-Output "2) php-7.3"
            $php = Read-Host ("selezione")
      }
      until ("1","2" -ccontains $php)
      $defaultPrefix = $folderPath -replace "/","."
      Write-Output "Inserisci il prefisso del sito (che andra' a comporre l'url: prefisso.$hostname.$domain)"
      $prefix = Read-Host "(default $defaultPrefix)"
      if ($prefix.Length -eq 0){
            $prefix = $defaultPrefix
      }
      if($prefix -eq "default"){
            Write-Error "Attenzione! non puoi sovrascrivere il sito di default"
            Write-Output "azione annullata"
            return
      }
      if((Get-DockerWebsiteName).Contains($prefix)){
            Write-Warning "Il file $prefix.conf esiste già"
            if(!(Read-YesNo "Sovrascriverlo?")){
                  Write-Output "azione annullata"
                  return
            }
      }

      $addToHosts = Read-YesNo "Vuoi aggiungere il puntamento del sito al file hosts?"

      $fullUrl = "$prefix.$hostname.$domain"
      Write-Output "Configuring $fullUrl"
      Write-Output "Generating .conf file"

      if($php -eq 1){
            $template = Join-Path $dockerEnv.templatePath "template-php71"
      }else{
            $template = Join-Path $dockerEnv.templatePath "template-php73"
      }
      if(!$laravelSite){
            $template = $template+"-base"
      }
      $template = $template+".conf"
      $relativePath = "/var/www/$folderPath"
      if($laravelSite){
            $relativePath = $relativePath+"/public"
      }

      if($addToHosts){
            $winHostPath = "C:/Windows/System32/drivers/etc/hosts"
            $hostPath = $winHostPath
            if($isLinux){
                  $hostPath = "/etc/hosts"
                  $isWsl = Test-Path ("/mnt/c/Windows/System32/drivers/etc/hosts") -PathType Leaf
                  if($isWsl){
                        $command = "gsudo `"add-content -Path $winHostPath -Value '127.0.0.1 $fullUrl'`""
                        $bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
                        $encodedCommand = [Convert]::ToBase64String($bytes)
                        /mnt/c/'Program Files'/PowerShell/7/pwsh.exe -encodedCommand $encodedCommand
                  }
            }
            "127.0.0.1 $fullUrl" >> ${hostPath}
      }

      $outTemplate = (Get-Content $template) -replace "##SITE_NAME##",$fullUrl -replace "##SITE_PATH##",$relativePath

      $outTemplate > (Join-Path $dockerEnv.vhostsPath "$prefix.conf")

      Write-Output "created file " (Join-Path $dockerEnv.vhostsPath "$prefix.conf")
      Update-DockerWebsiteList | Out-Null
      Write-Output "Site List refreshed"
      Write-Output "Restarting Docker"
      
      Invoke-Expression "docker restart nginx"
      
      Write-Output "Done!"
}

function Add-DockerProxy{
      param($port)
      $dockerEnv = Get-DockerEnv
      $hostname = $dockerEnv.PC_NAME
      $domain = $dockerEnv.WEBSITE_DOMAIN
      $prefix = ""
      while($prefix.Length -eq 0){
            Write-Output "Inserisci il prefisso del sito (che andra' a comporre l'url: prefisso.$hostname.$domain)"
            $prefix = Read-Host "prefisso:"
      }
      
      if($prefix -eq "default"){
            Write-Error "Attenzione! non puoi sovrascrivere il sito di default"
            Write-Output "azione annullata"
            return
      }
      if((Get-DockerWebsiteName).Contains($prefix)){
            Write-Warning "Il file $prefix.conf esiste già"
            if(!(Read-YesNo "Sovrascriverlo?")){
                  Write-Output "azione annullata"
                  return
            }
      }

      $addToHosts = Read-YesNo "Vuoi aggiungere il puntamento del sito al file hosts?"

      $fullUrl = "$prefix.$hostname.$domain"
      Write-Output "Configuring $fullUrl"
      Write-Output "Generating .conf file"

      $template = Join-Path $dockerEnv.templatePath "template-reverse"
      
      $template = $template+".conf"
      

      if($addToHosts){
            $winHostPath = "C:/Windows/System32/drivers/etc/hosts"
            $hostPath = $winHostPath
            if($isLinux){
                  $hostPath = "/etc/hosts"
                  $isWsl = Test-Path ("/mnt/c/Windows/System32/drivers/etc/hosts") -PathType Leaf
                  if($isWsl){
                        $command = "gsudo `"add-content -Path $winHostPath -Value '127.0.0.1 $fullUrl'`""
                        $bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
                        $encodedCommand = [Convert]::ToBase64String($bytes)
                        /mnt/c/'Program Files'/PowerShell/7/pwsh.exe -encodedCommand $encodedCommand
                  }
            }
            "127.0.0.1 $fullUrl" >> ${hostPath}
      }

      $outTemplate = (Get-Content $template) -replace "##SITE_NAME##",$fullUrl -replace "##PORT##",$port

      $outTemplate > (Join-Path $dockerEnv.vhostsPath "$prefix.conf")

      Write-Output "created file " (Join-Path $dockerEnv.vhostsPath "$prefix.conf")
      Update-DockerWebsiteList | Out-Null
      Write-Output "Site List refreshed"
      Write-Output "Restarting Docker"
      Invoke-Expression "docker restart nginx"
      Write-Output "Done!"
}

function Open-DockerDev{
      param(
            $path
      )
      $dockerEnv = Get-DockerEnv
      $devPath = (Join-Path $dockerEnv.WEBSITE_LOCAL_PATH $path) -replace "\\", "/"
      $containerName = ((Format-Hex -InputObject $env:TXD_CONTAINER_NAME).Bytes |ForEach-Object ToString X2) -join ""
      invoke-expression "code --remote=attached-container+$containerName $devPath"
} 
function Open-DockerDevShell{
      param(
            $path
      )
      $dockerEnv = Get-DockerEnv
      $devPath = (Join-Path $dockerEnv.WEBSITE_LOCAL_PATH $path) -replace "\\", "/"
      invoke-expression "docker exec -it -w $devPath php-dev /bin/bash"
} 
function Open-DockerHostFolder{
      param(
            $path
      )
      $fullPath = join-path $env:TXD_HOST_FOLDER_PATH $path
      invoke-expression "start $fullPath"
}

function Install-RecommendedCodeExtensions{
      code --install-extension bmewburn.vscode-intelephense-client --install-extension felixfbecker.php-debug --install-extension ronvanderheijden.phpdoc-generator --install-extension amiralizadeh9480.laravel-extra-intellisense --install-extension christian-kohler.path-intellisense --install-extension codingyu.laravel-goto-view --install-extension stef-k.laravel-goto-controller --install-extension onecentlin.laravel-blade --install-extension formulahendry.auto-complete-tag --install-extension mikestead.dotenv --install-extension eriklynd.json-tools --install-extension eamodio.gitlens --install-extension mhutchie.git-graph --install-extension ryannaddy.laravel-artisan
}

function Move-DockerProject(){
      Param (
          $path
      )
      if(-not $isLinux){
            write-host "This command can only be used inside the dev container"
            return $false
      }
      $dockerEnv = Get-DockerEnv
      $source = (join-path $dockerEnv.WEBSITE_INTERNAL_MIGRATION_PATH $path) -replace "\\", "/"
      $target = (join-path $dockerEnv.WEBSITE_LOCAL_PATH $path) -replace "\\", "/"
      if(-not (Test-Path -PathType Container -Path $target)){
          New-Item -ItemType Directory -Force -Path $target
          push-location $source
          $remotes = git remote -v
          $remote = $remotes | where-object {$_.contains("origin")} | Select-Object -First 1
          $remote = $remote.Split(" ")[0]
  
          pop-location
          push-location $target
          invoke-expression "git clone -o $remote ."
          pop-location
          Copy-Item (join-path $source ".env") (join-path $target ".env")
          Copy-Item (join-path $source "app/Http/Controllers/TestController.php") (join-path $target "app/Http/Controllers/TestController.php")
          return $true
      }else{
          write-host "$target already exists"
      }
      return $false;
}

$rm_block = {
      param($commandName, $wordToComplete)
      $list = Get-DockerWebsiteName -noDefault
      if($commandName.Length -gt 0 ){
            $list = $list | Select-String -Pattern $commandName
      }
      $list | Sort-Object
  }


$dockerdev_block = {
      param($commandName,$parameterName, $wordToComplete)
      
      $subfolder = "/"
      $matchingWord = $wordToComplete
      if($wordToComplete -match '(.*)\/(.*)'){
            
            $subfolder = $Matches.1
            $matchingWord = $Matches.2
      }
      
      $fullpath = (join-path $env:TXD_HOST_FOLDER_PATH $subfolder)
      
      $list = Get-ChildItem -directory $fullpath | ForEach-Object {$_.Name}
      if($matchingWord.Length -gt 0 ){
            $list = $list | Select-String -Pattern $matchingWord
      }
      $list | Sort-Object | ForEach-Object {
            $fullParam = (Join-path  $subfolder $_"/")  -replace "\\", "/"
            New-Object -Type System.Management.Automation.CompletionResult -ArgumentList $fullParam,
              $_,
              "ParameterValue",
              $fullParam
        }
}

$moveproject_block = {
      param($commandName,$parameterName, $wordToComplete)
      $dockerEnv = Get-DockerEnv
      $subfolder = "/"
      $matchingWord = $wordToComplete
      if($wordToComplete -match '(.*)\/(.*)'){
            
            $subfolder = $Matches.1
            $matchingWord = $Matches.2
      }
      
      $fullpath = (join-path $dockerEnv.WEBSITE_INTERNAL_MIGRATION_PATH $subfolder)
      
      $list = Get-ChildItem -directory $fullpath | ForEach-Object {$_.Name}
      if($matchingWord.Length -gt 0 ){
            $list = $list | Select-String -Pattern $matchingWord
      }
      $list | Sort-Object | ForEach-Object {
            $fullParam = (Join-path  $subfolder $_"/")  -replace "\\", "/"
            New-Object -Type System.Management.Automation.CompletionResult -ArgumentList $fullParam,
              $_,
              "ParameterValue",
              $fullParam
        }
}

  
Register-ArgumentCompleter -Native -CommandName Remove-DockerWebsite -ScriptBlock $rm_block
Register-ArgumentCompleter -CommandName Open-DockerDev -ParameterName "path" -ScriptBlock $dockerdev_block
Register-ArgumentCompleter -CommandName Open-DockerDevShell -ParameterName "path" -ScriptBlock $dockerdev_block
Register-ArgumentCompleter -CommandName Open-DockerHostFolder -ParameterName "path" -ScriptBlock $dockerdev_block
Register-ArgumentCompleter -CommandName Move-DockerProject -ParameterName "path" -ScriptBlock $moveproject_block
Export-ModuleMember -Function Get-DockerEnv, get-DockerArtisan, Add-DockerWebsite, Add-DockerProxy, Update-DockerWebsiteList, Restart-DockerWebserver, Remove-DockerWebsite, Open-DockerDev,Open-DockerDevShell, Open-DockerHostFolder,Install-RecommendedCodeExtensions, Move-DockerProject -Alias *