44
55use Appwrite \AppwriteException ;
66use Appwrite \Client ;
7+ use Appwrite \Enums \Adapter ;
8+ use Appwrite \Enums \BuildRuntime ;
79use Appwrite \Enums \Compression ;
10+ use Appwrite \Enums \Framework ;
811use Appwrite \Enums \PasswordHash ;
912use Appwrite \Enums \Runtime ;
1013use Appwrite \InputFile ;
1114use Appwrite \Services \Functions ;
15+ use Appwrite \Services \Sites ;
1216use Appwrite \Services \Storage ;
1317use Appwrite \Services \Teams ;
1418use Appwrite \Services \Users ;
4347use Utopia \Migration \Resources \Functions \Deployment ;
4448use Utopia \Migration \Resources \Functions \EnvVar ;
4549use Utopia \Migration \Resources \Functions \Func ;
50+ use Utopia \Migration \Resources \Sites \Deployment as SiteDeployment ;
51+ use Utopia \Migration \Resources \Sites \EnvVar as SiteEnvVar ;
52+ use Utopia \Migration \Resources \Sites \Site ;
4653use Utopia \Migration \Resources \Storage \Bucket ;
4754use Utopia \Migration \Resources \Storage \File ;
4855use Utopia \Migration \Transfer ;
@@ -55,6 +62,7 @@ class Appwrite extends Destination
5562 protected string $ key ;
5663
5764 private Functions $ functions ;
65+ private Sites $ sites ;
5866 private Storage $ storage ;
5967 private Teams $ teams ;
6068 private Users $ users ;
@@ -88,6 +96,7 @@ public function __construct(
8896 ->setKey ($ key );
8997
9098 $ this ->functions = new Functions ($ this ->client );
99+ $ this ->sites = new Sites ($ this ->client );
91100 $ this ->storage = new Storage ($ this ->client );
92101 $ this ->teams = new Teams ($ this ->client );
93102 $ this ->users = new Users ($ this ->client );
@@ -132,6 +141,11 @@ public static function getSupportedResources(): array
132141
133142 // Backups
134143 Resource::TYPE_BACKUP_POLICY ,
144+
145+ // Sites
146+ Resource::TYPE_SITE ,
147+ Resource::TYPE_SITE_DEPLOYMENT ,
148+ Resource::TYPE_SITE_VARIABLE ,
135149 ];
136150 }
137151
@@ -203,6 +217,15 @@ public function report(array $resources = [], array $resourceIds = []): array
203217 $ this ->functions ->create ('' , '' , Runtime::NODE180 ());
204218 }
205219
220+ // Sites
221+ if (\in_array (Resource::TYPE_SITE , $ resources )) {
222+ $ scope = 'sites.read ' ;
223+ $ this ->sites ->list ();
224+
225+ $ scope = 'sites.write ' ;
226+ $ this ->sites ->create ('' , '' , Framework::OTHER (), BuildRuntime::STATIC1 ());
227+ }
228+
206229 } catch (AppwriteException $ e ) {
207230 if ($ e ->getCode () === 403 ) {
208231 throw new \Exception ('Missing scope: ' . $ scope , previous: $ e );
@@ -274,6 +297,7 @@ protected function import(array $resources, callable $callback): void
274297 Transfer::GROUP_AUTH => $ this ->importAuthResource ($ resource ),
275298 Transfer::GROUP_FUNCTIONS => $ this ->importFunctionResource ($ resource ),
276299 Transfer::GROUP_BACKUPS => $ this ->importBackupResource ($ resource ),
300+ Transfer::GROUP_SITES => $ this ->importSiteResource ($ resource ),
277301 default => throw new \Exception ('Invalid resource group ' ),
278302 };
279303 } catch (\Throwable $ e ) {
@@ -1607,4 +1631,205 @@ private function importDeployment(Deployment $deployment): Resource
16071631
16081632 return $ deployment ;
16091633 }
1634+
1635+ /**
1636+ * @throws AppwriteException
1637+ */
1638+ public function importSiteResource (Resource $ resource ): Resource
1639+ {
1640+ switch ($ resource ->getName ()) {
1641+ case Resource::TYPE_SITE :
1642+ /** @var Site $resource */
1643+
1644+ $ buildRuntime = match ($ resource ->getBuildRuntime ()) {
1645+ 'node-14.5 ' => BuildRuntime::NODE145 (),
1646+ 'node-16.0 ' => BuildRuntime::NODE160 (),
1647+ 'node-18.0 ' => BuildRuntime::NODE180 (),
1648+ 'node-19.0 ' => BuildRuntime::NODE190 (),
1649+ 'node-20.0 ' => BuildRuntime::NODE200 (),
1650+ 'node-21.0 ' => BuildRuntime::NODE210 (),
1651+ 'node-22 ' => BuildRuntime::NODE22 (),
1652+ 'php-8.0 ' => BuildRuntime::PHP80 (),
1653+ 'php-8.1 ' => BuildRuntime::PHP81 (),
1654+ 'php-8.2 ' => BuildRuntime::PHP82 (),
1655+ 'php-8.3 ' => BuildRuntime::PHP83 (),
1656+ 'ruby-3.0 ' => BuildRuntime::RUBY30 (),
1657+ 'ruby-3.1 ' => BuildRuntime::RUBY31 (),
1658+ 'ruby-3.2 ' => BuildRuntime::RUBY32 (),
1659+ 'ruby-3.3 ' => BuildRuntime::RUBY33 (),
1660+ 'python-3.8 ' => BuildRuntime::PYTHON38 (),
1661+ 'python-3.9 ' => BuildRuntime::PYTHON39 (),
1662+ 'python-3.10 ' => BuildRuntime::PYTHON310 (),
1663+ 'python-3.11 ' => BuildRuntime::PYTHON311 (),
1664+ 'python-3.12 ' => BuildRuntime::PYTHON312 (),
1665+ 'python-ml-3.11 ' => BuildRuntime::PYTHONML311 (),
1666+ 'python-ml-3.12 ' => BuildRuntime::PYTHONML312 (),
1667+ 'dart-3.0 ' => BuildRuntime::DART30 (),
1668+ 'dart-3.1 ' => BuildRuntime::DART31 (),
1669+ 'dart-3.3 ' => BuildRuntime::DART33 (),
1670+ 'dart-3.5 ' => BuildRuntime::DART35 (),
1671+ 'dart-3.8 ' => BuildRuntime::DART38 (),
1672+ 'dart-3.9 ' => BuildRuntime::DART39 (),
1673+ 'dart-2.15 ' => BuildRuntime::DART215 (),
1674+ 'dart-2.16 ' => BuildRuntime::DART216 (),
1675+ 'dart-2.17 ' => BuildRuntime::DART217 (),
1676+ 'dart-2.18 ' => BuildRuntime::DART218 (),
1677+ 'dart-2.19 ' => BuildRuntime::DART219 (),
1678+ 'deno-1.21 ' => BuildRuntime::DENO121 (),
1679+ 'deno-1.24 ' => BuildRuntime::DENO124 (),
1680+ 'deno-1.35 ' => BuildRuntime::DENO135 (),
1681+ 'deno-1.40 ' => BuildRuntime::DENO140 (),
1682+ 'deno-1.46 ' => BuildRuntime::DENO146 (),
1683+ 'deno-2.0 ' => BuildRuntime::DENO20 (),
1684+ 'dotnet-6.0 ' => BuildRuntime::DOTNET60 (),
1685+ 'dotnet-7.0 ' => BuildRuntime::DOTNET70 (),
1686+ 'dotnet-8.0 ' => BuildRuntime::DOTNET80 (),
1687+ 'java-8.0 ' => BuildRuntime::JAVA80 (),
1688+ 'java-11.0 ' => BuildRuntime::JAVA110 (),
1689+ 'java-17.0 ' => BuildRuntime::JAVA170 (),
1690+ 'java-18.0 ' => BuildRuntime::JAVA180 (),
1691+ 'java-21.0 ' => BuildRuntime::JAVA210 (),
1692+ 'java-22 ' => BuildRuntime::JAVA22 (),
1693+ 'swift-5.5 ' => BuildRuntime::SWIFT55 (),
1694+ 'swift-5.8 ' => BuildRuntime::SWIFT58 (),
1695+ 'swift-5.9 ' => BuildRuntime::SWIFT59 (),
1696+ 'swift-5.10 ' => BuildRuntime::SWIFT510 (),
1697+ 'kotlin-1.6 ' => BuildRuntime::KOTLIN16 (),
1698+ 'kotlin-1.8 ' => BuildRuntime::KOTLIN18 (),
1699+ 'kotlin-1.9 ' => BuildRuntime::KOTLIN19 (),
1700+ 'kotlin-2.0 ' => BuildRuntime::KOTLIN20 (),
1701+ 'cpp-17 ' => BuildRuntime::CPP17 (),
1702+ 'cpp-20 ' => BuildRuntime::CPP20 (),
1703+ 'bun-1.0 ' => BuildRuntime::BUN10 (),
1704+ 'bun-1.1 ' => BuildRuntime::BUN11 (),
1705+ 'go-1.23 ' => BuildRuntime::GO123 (),
1706+ 'static-1 ' => BuildRuntime::STATIC1 (),
1707+ 'flutter-3.24 ' => BuildRuntime::FLUTTER324 (),
1708+ 'flutter-3.27 ' => BuildRuntime::FLUTTER327 (),
1709+ 'flutter-3.29 ' => BuildRuntime::FLUTTER329 (),
1710+ 'flutter-3.32 ' => BuildRuntime::FLUTTER332 (),
1711+ 'flutter-3.35 ' => BuildRuntime::FLUTTER335 (),
1712+ default => throw new \Exception ('Invalid Build Runtime: ' . $ resource ->getBuildRuntime ()),
1713+ };
1714+
1715+ $ framework = match ($ resource ->getFramework ()) {
1716+ 'analog ' => Framework::ANALOG (),
1717+ 'angular ' => Framework::ANGULAR (),
1718+ 'astro ' => Framework::ASTRO (),
1719+ 'flutter ' , 'flutter-web ' => Framework::FLUTTER (),
1720+ 'lynx ' => Framework::LYNX (),
1721+ 'nextjs ' => Framework::NEXTJS (),
1722+ 'nuxt ' => Framework::NUXT (),
1723+ 'react ' => Framework::REACT (),
1724+ 'react-native ' => Framework::REACTNATIVE (),
1725+ 'remix ' => Framework::REMIX (),
1726+ 'svelte-kit ' => Framework::SVELTEKIT (),
1727+ 'tanstack-start ' => Framework::TANSTACKSTART (),
1728+ 'vite ' => Framework::VITE (),
1729+ 'vue ' => Framework::VUE (),
1730+ default => Framework::OTHER (),
1731+ };
1732+
1733+ $ adapter = match ($ resource ->getAdapter ()) {
1734+ 'static ' => Adapter::STATIC (),
1735+ 'ssr ' => Adapter::SSR (),
1736+ default => null ,
1737+ };
1738+
1739+ $ this ->sites ->create (
1740+ $ resource ->getId (),
1741+ $ resource ->getSiteName (),
1742+ $ framework ,
1743+ $ buildRuntime ,
1744+ $ resource ->getEnabled (),
1745+ $ resource ->getLogging (),
1746+ $ resource ->getTimeout (),
1747+ $ resource ->getInstallCommand (),
1748+ $ resource ->getBuildCommand (),
1749+ $ resource ->getOutputDirectory (),
1750+ $ adapter ,
1751+ fallbackFile: $ resource ->getFallbackFile (),
1752+ specification: $ resource ->getSpecification (),
1753+ );
1754+ break ;
1755+ case Resource::TYPE_SITE_VARIABLE :
1756+ /** @var SiteEnvVar $resource */
1757+ $ this ->sites ->createVariable (
1758+ $ resource ->getSite ()->getId (),
1759+ $ resource ->getKey (),
1760+ $ resource ->getValue ()
1761+ );
1762+ break ;
1763+ case Resource::TYPE_SITE_DEPLOYMENT :
1764+ /** @var SiteDeployment $resource */
1765+ return $ this ->importSiteDeployment ($ resource );
1766+ }
1767+
1768+ $ resource ->setStatus (Resource::STATUS_SUCCESS );
1769+
1770+ return $ resource ;
1771+ }
1772+
1773+ /**
1774+ * @throws AppwriteException
1775+ * @throws \Exception
1776+ */
1777+ private function importSiteDeployment (SiteDeployment $ deployment ): Resource
1778+ {
1779+ $ siteId = $ deployment ->getSite ()->getId ();
1780+
1781+ if ($ deployment ->getSize () <= Transfer::STORAGE_MAX_CHUNK_SIZE ) {
1782+ $ response = $ this ->client ->call (
1783+ 'POST ' ,
1784+ "/sites/ {$ siteId }/deployments " ,
1785+ [
1786+ 'content-type ' => 'multipart/form-data ' ,
1787+ ],
1788+ [
1789+ 'siteId ' => $ siteId ,
1790+ 'code ' => new \CURLFile ('data://application/gzip;base64, ' . base64_encode ($ deployment ->getData ()), 'application/gzip ' , 'deployment.tar.gz ' ),
1791+ 'activate ' => $ deployment ->getActivated (),
1792+ ]
1793+ );
1794+
1795+ if (!\is_array ($ response ) || !isset ($ response ['$id ' ])) {
1796+ throw new \Exception ('Site deployment creation failed ' );
1797+ }
1798+
1799+ $ deployment ->setStatus (Resource::STATUS_SUCCESS );
1800+
1801+ return $ deployment ;
1802+ }
1803+
1804+ $ response = $ this ->client ->call (
1805+ 'POST ' ,
1806+ "/sites/ {$ siteId }/deployments " ,
1807+ [
1808+ 'content-type ' => 'multipart/form-data ' ,
1809+ 'content-range ' => 'bytes ' . ($ deployment ->getStart ()) . '- ' . ($ deployment ->getEnd () == ($ deployment ->getSize () - 1 ) ? $ deployment ->getSize () : $ deployment ->getEnd ()) . '/ ' . $ deployment ->getSize (),
1810+ 'x-appwrite-id ' => $ deployment ->getId (),
1811+ ],
1812+ [
1813+ 'siteId ' => $ siteId ,
1814+ 'code ' => new \CURLFile ('data://application/gzip;base64, ' . base64_encode ($ deployment ->getData ()), 'application/gzip ' , 'deployment.tar.gz ' ),
1815+ 'activate ' => $ deployment ->getActivated (),
1816+ ]
1817+ );
1818+
1819+ if (!\is_array ($ response ) || !isset ($ response ['$id ' ])) {
1820+ throw new \Exception ('Site deployment creation failed ' );
1821+ }
1822+
1823+ if ($ deployment ->getStart () === 0 ) {
1824+ $ deployment ->setId ($ response ['$id ' ]);
1825+ }
1826+
1827+ if ($ deployment ->getEnd () == ($ deployment ->getSize () - 1 )) {
1828+ $ deployment ->setStatus (Resource::STATUS_SUCCESS );
1829+ } else {
1830+ $ deployment ->setStatus (Resource::STATUS_PENDING );
1831+ }
1832+
1833+ return $ deployment ;
1834+ }
16101835}
0 commit comments