mirror of
https://github.com/appwrite/appwrite
synced 2026-05-18 14:38:35 +00:00
Resolve merge conflicts
This commit is contained in:
commit
ace7a6baa1
25 changed files with 389 additions and 124 deletions
|
|
@ -837,7 +837,7 @@ return [
|
|||
Exception::RULE_VERIFICATION_FAILED => [
|
||||
'name' => Exception::RULE_VERIFICATION_FAILED,
|
||||
'description' => 'Domain verification failed. Please check if your DNS records are correct and try again.',
|
||||
'code' => 401,
|
||||
'code' => 400,
|
||||
'publish' => true
|
||||
],
|
||||
Exception::PROJECT_SMTP_CONFIG_INVALID => [
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ return [
|
|||
'vue' => [
|
||||
'key' => 'vue',
|
||||
'name' => 'Vue.js',
|
||||
'screenshotSleep' => 3000,
|
||||
'screenshotSleep' => 5000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'adapters' => [
|
||||
|
|
@ -227,6 +227,23 @@ return [
|
|||
]
|
||||
]
|
||||
],
|
||||
'lynx' => [
|
||||
'key' => 'lynx',
|
||||
'name' => 'Lynx',
|
||||
'screenshotSleep' => 5000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './dist',
|
||||
'startCommand' => 'bash helpers/server.sh',
|
||||
'fallbackFile' => 'index.html'
|
||||
]
|
||||
]
|
||||
],
|
||||
'flutter' => [
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
|
|
|
|||
|
|
@ -24718,6 +24718,7 @@
|
|||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
|
|
@ -25353,6 +25354,7 @@
|
|||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
|
|
@ -37040,6 +37042,11 @@
|
|||
"description": "Site Template Name.",
|
||||
"x-example": "Starter site"
|
||||
},
|
||||
"tagline": {
|
||||
"type": "string",
|
||||
"description": "Short description of template",
|
||||
"x-example": "Minimal web app integrating with Appwrite."
|
||||
},
|
||||
"demoUrl": {
|
||||
"type": "string",
|
||||
"description": "URL hosting a template demo.",
|
||||
|
|
@ -37103,6 +37110,7 @@
|
|||
"required": [
|
||||
"key",
|
||||
"name",
|
||||
"tagline",
|
||||
"demoUrl",
|
||||
"screenshotDark",
|
||||
"screenshotLight",
|
||||
|
|
|
|||
|
|
@ -16816,6 +16816,7 @@
|
|||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
|
|
@ -17226,6 +17227,7 @@
|
|||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
|
|
|
|||
|
|
@ -25221,6 +25221,7 @@
|
|||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
|
|
@ -25871,6 +25872,7 @@
|
|||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
|
|
@ -37623,6 +37625,11 @@
|
|||
"description": "Site Template Name.",
|
||||
"x-example": "Starter site"
|
||||
},
|
||||
"tagline": {
|
||||
"type": "string",
|
||||
"description": "Short description of template",
|
||||
"x-example": "Minimal web app integrating with Appwrite."
|
||||
},
|
||||
"demoUrl": {
|
||||
"type": "string",
|
||||
"description": "URL hosting a template demo.",
|
||||
|
|
@ -37688,6 +37695,7 @@
|
|||
"required": [
|
||||
"key",
|
||||
"name",
|
||||
"tagline",
|
||||
"demoUrl",
|
||||
"screenshotDark",
|
||||
"screenshotLight",
|
||||
|
|
|
|||
|
|
@ -17285,6 +17285,7 @@
|
|||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
|
|
@ -17714,6 +17715,7 @@
|
|||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ return [
|
|||
'icon' => 'icon-lightning-bolt',
|
||||
'id' => 'starter',
|
||||
'name' => 'Starter function',
|
||||
'score' => 5,
|
||||
'tagline' =>
|
||||
'A simple function to get started. Edit this function to explore endless possibilities with Appwrite Functions.',
|
||||
'permissions' => ['any'],
|
||||
|
|
@ -62,6 +63,7 @@ return [
|
|||
'icon' => 'icon-upstash',
|
||||
'id' => 'query-upstash-vector',
|
||||
'name' => 'Query Upstash Vector',
|
||||
'score' => 4,
|
||||
'tagline' => 'Vector database that stores text embeddings and context retrieval for LLMs',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -106,6 +108,7 @@ return [
|
|||
'icon' => 'icon-redis',
|
||||
'id' => 'query-redis-labs',
|
||||
'name' => 'Query Redis Labs',
|
||||
'score' => 4,
|
||||
'tagline' => 'Key-value database with advanced caching capabilities.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -149,6 +152,7 @@ return [
|
|||
'icon' => 'icon-neo4j',
|
||||
'id' => 'query-neo4j-auradb',
|
||||
'name' => 'Query Neo4j AuraDB',
|
||||
'score' => 4,
|
||||
'tagline' => 'Graph database with focus on relations between data.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -200,6 +204,7 @@ return [
|
|||
'icon' => 'icon-mongodb',
|
||||
'id' => 'query-mongo-atlas',
|
||||
'name' => 'Query MongoDB Atlas',
|
||||
'score' => 4,
|
||||
'tagline' =>
|
||||
'Realtime NoSQL document database with geospecial, graph, search, and vector suport.',
|
||||
'permissions' => ['any'],
|
||||
|
|
@ -237,6 +242,7 @@ return [
|
|||
'icon' => 'icon-neon',
|
||||
'id' => 'query-neon-postgres',
|
||||
'name' => 'Query Neon Postgres',
|
||||
'score' => 4,
|
||||
'tagline' =>
|
||||
'Reliable SQL database with replication, point-in-time recovery, and pgvector support.',
|
||||
'permissions' => ['any'],
|
||||
|
|
@ -305,6 +311,7 @@ return [
|
|||
'icon' => 'icon-open-ai',
|
||||
'id' => 'prompt-chatgpt',
|
||||
'name' => 'Prompt ChatGPT',
|
||||
'score' => 7,
|
||||
'tagline' => 'Ask questions and let OpenAI GPT-3.5-turbo answer.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -366,6 +373,7 @@ return [
|
|||
'icon' => 'icon-discord',
|
||||
'id' => 'discord-command-bot',
|
||||
'name' => 'Discord Command Bot',
|
||||
'score' => 6,
|
||||
'tagline' => 'Simple command using Discord Interactions.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -429,6 +437,7 @@ return [
|
|||
'icon' => 'icon-perspective-api',
|
||||
'id' => 'analyze-with-perspectiveapi',
|
||||
'name' => 'Analyze with PerspectiveAPI',
|
||||
'score' => 5,
|
||||
'tagline' => 'Automate moderation by getting toxicity of messages.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -464,6 +473,7 @@ return [
|
|||
'icon' => 'icon-pangea',
|
||||
'id' => 'censor-with-redact',
|
||||
'name' => 'Censor with Redact',
|
||||
'score' => 5,
|
||||
'tagline' =>
|
||||
'Censor sensitive information from a provided text string using Redact API by Pangea.',
|
||||
'permissions' => ['any'],
|
||||
|
|
@ -512,6 +522,7 @@ return [
|
|||
'icon' => 'icon-document',
|
||||
'id' => 'generate-pdf',
|
||||
'name' => 'Generate PDF',
|
||||
'score' => 7,
|
||||
'tagline' => 'Document containing sample invoice in PDF format.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -533,6 +544,7 @@ return [
|
|||
'icon' => 'icon-github',
|
||||
'id' => 'github-issue-bot',
|
||||
'name' => 'GitHub issue bot',
|
||||
'score' => 4,
|
||||
'tagline' =>
|
||||
'Automate the process of responding to newly opened issues in a GitHub repository.',
|
||||
'permissions' => ['any'],
|
||||
|
|
@ -577,6 +589,7 @@ return [
|
|||
'icon' => 'icon-bookmark',
|
||||
'id' => 'url-shortener',
|
||||
'name' => 'URL shortener',
|
||||
'score' => 3,
|
||||
'tagline' => 'Generate URL with short ID and redirect to the original URL when visited.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -628,6 +641,7 @@ return [
|
|||
'icon' => 'icon-algolia',
|
||||
'id' => 'sync-with-algolia',
|
||||
'name' => 'Sync with Algolia',
|
||||
'score' => 4,
|
||||
'tagline' => 'Intuitive search bar for any data in Appwrite Databases.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -709,6 +723,7 @@ return [
|
|||
'icon' => 'icon-meilisearch',
|
||||
'id' => 'sync-with-meilisearch',
|
||||
'name' => 'Sync with Meilisearch',
|
||||
'score' => 4,
|
||||
'tagline' => 'Intuitive search bar for any data in Appwrite Databases.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -802,6 +817,7 @@ return [
|
|||
'icon' => 'icon-vonage',
|
||||
'id' => 'whatsapp-with-vonage',
|
||||
'name' => 'WhatsApp with Vonage',
|
||||
'score' => 6,
|
||||
'tagline' => 'Simple bot to answer WhatsApp messages.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -888,6 +904,7 @@ return [
|
|||
'icon' => 'icon-bell',
|
||||
'id' => 'push-notification-with-fcm',
|
||||
'name' => 'Push notification with FCM',
|
||||
'score' => 4,
|
||||
'tagline' => 'Send push notifications to your users using Firebase Cloud Messaging (FCM).',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -944,6 +961,7 @@ return [
|
|||
'icon' => 'icon-mail',
|
||||
'id' => 'email-contact-form',
|
||||
'name' => 'Email contact form',
|
||||
'score' => 7,
|
||||
'tagline' => 'Sends an email with the contents of a HTML form.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1027,6 +1045,7 @@ return [
|
|||
'icon' => 'icon-stripe',
|
||||
'id' => 'subscriptions-with-stripe',
|
||||
'name' => 'Subscriptions with Stripe',
|
||||
'score' => 6,
|
||||
'tagline' => 'Receive recurring card payments and grant subscribers extra permissions.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1068,6 +1087,7 @@ return [
|
|||
'icon' => 'icon-stripe',
|
||||
'id' => 'payments-with-stripe',
|
||||
'name' => 'Payments with Stripe',
|
||||
'score' => 8,
|
||||
'tagline' => 'Receive card payments and store paid orders.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1125,6 +1145,7 @@ return [
|
|||
'icon' => 'icon-chat',
|
||||
'id' => 'text-generation-with-huggingface',
|
||||
'name' => 'Text generation',
|
||||
'score' => 5,
|
||||
'tagline' => 'Generate text using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1159,6 +1180,7 @@ return [
|
|||
'icon' => 'icon-translate',
|
||||
'id' => 'language-translation-with-huggingface',
|
||||
'name' => 'Language translation',
|
||||
'score' => 5,
|
||||
'tagline' => 'Translate text using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1193,6 +1215,7 @@ return [
|
|||
'icon' => 'icon-eye',
|
||||
'id' => 'image-classification-with-huggingface',
|
||||
'name' => 'Image classification',
|
||||
'score' => 5,
|
||||
'tagline' => 'Classify images using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => ['buckets.*.files.*.create'],
|
||||
|
|
@ -1251,6 +1274,7 @@ return [
|
|||
'icon' => 'icon-eye',
|
||||
'id' => 'object-detection-with-huggingface',
|
||||
'name' => 'Object detection',
|
||||
'score' => 5,
|
||||
'tagline' => 'Detect objects in images using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => ['buckets.*.files.*.create'],
|
||||
|
|
@ -1309,6 +1333,7 @@ return [
|
|||
'icon' => 'icon-text',
|
||||
'id' => 'speech-recognition-with-huggingface',
|
||||
'name' => 'Speech recognition',
|
||||
'score' => 5,
|
||||
'tagline' => 'Transcribe audio to text using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => ['buckets.*.files.*.create'],
|
||||
|
|
@ -1367,6 +1392,7 @@ return [
|
|||
'icon' => 'icon-chat',
|
||||
'id' => 'text-to-speech-with-huggingface',
|
||||
'name' => 'Text to speech',
|
||||
'score' => 5,
|
||||
'tagline' => 'Convert text to speech using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => ['databases.*.collections.*.documents.*.create'],
|
||||
|
|
@ -1425,6 +1451,7 @@ return [
|
|||
'icon' => 'icon-chip',
|
||||
'id' => 'generate-with-replicate',
|
||||
'name' => 'Generate with Replicate',
|
||||
'score' => 5,
|
||||
'tagline' => "Generate text, audio and images using Replicate's API.",
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1460,6 +1487,7 @@ return [
|
|||
'icon' => 'icon-chip',
|
||||
'id' => 'generate-with-together-ai',
|
||||
'name' => 'Generate with Together AI',
|
||||
'score' => 5,
|
||||
'tagline' => "Generate text and images using Together AI's API.",
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1502,6 +1530,7 @@ return [
|
|||
'icon' => 'icon-chip',
|
||||
'id' => 'chat-with-perplexity-ai',
|
||||
'name' => 'Chat with Perplexity AI',
|
||||
'score' => 5,
|
||||
'tagline' => 'Create a chatbot using the Perplexity AI API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1543,6 +1572,7 @@ return [
|
|||
'icon' => 'icon-chip',
|
||||
'id' => 'generate-with-replicate',
|
||||
'name' => 'Generate with Replicate',
|
||||
'score' => 5,
|
||||
'tagline' => "Generate text, audio and images using Replicate's API.",
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1578,6 +1608,7 @@ return [
|
|||
'icon' => 'icon-document-search',
|
||||
'id' => 'sync-with-pinecone',
|
||||
'name' => 'Sync with Pinecone',
|
||||
'score' => 4,
|
||||
'tagline' => "Sync your Appwrite database with Pinecone's vector database.",
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1641,6 +1672,7 @@ return [
|
|||
'icon' => 'icon-chip',
|
||||
'id' => 'rag-with-langchain',
|
||||
'name' => 'RAG with LangChain',
|
||||
'score' => 6,
|
||||
'tagline' => 'Generate text using a LangChain RAG model',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1704,6 +1736,7 @@ return [
|
|||
'icon' => 'icon-chat',
|
||||
'id' => 'speak-with-elevenlabs',
|
||||
'name' => 'Speak with ElevenLabs',
|
||||
'score' => 5,
|
||||
'tagline' => 'Convert text to speech using the ElevenLabs API.',
|
||||
'permissions' => ['any'],
|
||||
'cron' => '',
|
||||
|
|
@ -1759,6 +1792,7 @@ return [
|
|||
'icon' => 'icon-chip',
|
||||
'id' => 'speak-with-lmnt',
|
||||
'name' => 'Speak with LMNT',
|
||||
'score' => 5,
|
||||
'tagline' => 'Convert text to speech using the LMNT API.',
|
||||
'permissions' => ['any'],
|
||||
'cron' => '',
|
||||
|
|
@ -1800,6 +1834,7 @@ return [
|
|||
'icon' => 'icon-chip',
|
||||
'id' => 'chat-with-anyscale',
|
||||
'name' => 'Chat with AnyScale',
|
||||
'score' => 5,
|
||||
'tagline' => 'Create a chatbot using the AnyScale API.',
|
||||
'permissions' => ['any'],
|
||||
'cron' => '',
|
||||
|
|
@ -1841,6 +1876,7 @@ return [
|
|||
'icon' => 'icon-music-note',
|
||||
'id' => 'music-generation-with-huggingface',
|
||||
'name' => 'Music generation',
|
||||
'score' => 4,
|
||||
'tagline' => 'Generate music from a text prompt using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1883,6 +1919,7 @@ return [
|
|||
'icon' => 'icon-chip',
|
||||
'id' => 'generate-with-fal-ai',
|
||||
'name' => 'Generate with fal.ai',
|
||||
'score' => 5,
|
||||
'tagline' => "Generate images using fal.ai's API.",
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1918,6 +1955,7 @@ return [
|
|||
'icon' => 'icon-currency-dollar',
|
||||
'id' => 'subscriptions-with-lemon-squeezy',
|
||||
'name' => 'Subscriptions with Lemon Squeezy',
|
||||
'score' => 6,
|
||||
'tagline' => 'Receive recurring card payments and grant subscribers extra permissions.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
@ -1973,6 +2011,7 @@ return [
|
|||
'icon' => 'icon-currency-dollar',
|
||||
'id' => 'payments-with-lemon-squeezy',
|
||||
'name' => 'Payments with Lemon Squeezy',
|
||||
'score' => 6,
|
||||
'tagline' => 'Receive card payments and store paid orders.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
|
|
|||
|
|
@ -87,15 +87,6 @@ const TEMPLATE_FRAMEWORKS = [
|
|||
'adapter' => 'static',
|
||||
'fallbackFile' => '',
|
||||
],
|
||||
'OTHER' => [
|
||||
'key' => 'other',
|
||||
'name' => 'Other',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'static',
|
||||
'fallbackFile' => 'index.html',
|
||||
],
|
||||
'VITE' => [
|
||||
'key' => 'vite',
|
||||
'name' => 'Vite',
|
||||
|
|
@ -144,6 +135,16 @@ const TEMPLATE_FRAMEWORKS = [
|
|||
'adapter' => 'static',
|
||||
'outputDirectory' => './',
|
||||
],
|
||||
'LYNX' => [
|
||||
'key' => 'lynx',
|
||||
'name' => 'Lynx',
|
||||
'installCommand' => 'npm install && cd web && npm install && cd ..',
|
||||
'buildCommand' => 'npm run build && cd web && npm run build && cd ..',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'static',
|
||||
'outputDirectory' => './web/dist',
|
||||
'fallbackFile' => 'index.html',
|
||||
],
|
||||
];
|
||||
|
||||
function getFramework(string $frameworkEnum, array $overrides)
|
||||
|
|
@ -153,9 +154,30 @@ function getFramework(string $frameworkEnum, array $overrides)
|
|||
}
|
||||
|
||||
return [
|
||||
[
|
||||
'key' => 'lynx-starter',
|
||||
'name' => 'Lynx Starter',
|
||||
'tagline' => 'Sample application built with Lynx, a cross-platform framework focused on performance.',
|
||||
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'screenshotDark' => $url . '/images/sites/templates/lynx-starter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/lynx-starter-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('LYNX', [
|
||||
'providerRootDirectory' => './lynx/starter',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates-for-sites',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.3.*',
|
||||
'variables' => []
|
||||
],
|
||||
[
|
||||
'key' => 'vitepress',
|
||||
'name' => 'Vitepress',
|
||||
'tagline' => 'Platform for documentation and knowledge sharing powered by Vite.',
|
||||
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::DOCUMENTATION],
|
||||
'screenshotDark' => $url . '/images/sites/templates/vitepress-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/vitepress-light.png',
|
||||
|
|
@ -177,6 +199,8 @@ return [
|
|||
[
|
||||
'key' => 'vuepress',
|
||||
'name' => 'Vuepress',
|
||||
'tagline' => 'Platform for documentation and knowledge sharing powered by Vue.',
|
||||
'score' => 4, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::DOCUMENTATION],
|
||||
'screenshotDark' => $url . '/images/sites/templates/vuepress-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/vuepress-light.png',
|
||||
|
|
@ -198,6 +222,8 @@ return [
|
|||
[
|
||||
'key' => 'docusaurus',
|
||||
'name' => 'Docusaurus',
|
||||
'tagline' => 'Platform for documentation and knowledge sharing powered by React.',
|
||||
'score' => 4, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::DOCUMENTATION],
|
||||
'screenshotDark' => $url . '/images/sites/templates/docusaurus-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/docusaurus-light.png',
|
||||
|
|
@ -219,6 +245,8 @@ return [
|
|||
[
|
||||
'key' => 'nxt-lnk',
|
||||
'name' => 'Nxt Lnk',
|
||||
'tagline' => 'Personal website for creators to merge all URLs to social profiles.',
|
||||
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/nxt-lnk-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/nxt-lnk-light.png',
|
||||
|
|
@ -236,6 +264,8 @@ return [
|
|||
[
|
||||
'key' => 'magic-portfolio',
|
||||
'name' => 'Magic Portfolio',
|
||||
'tagline' => 'Complex personal website to showcase your projects, articles, and more.',
|
||||
'score' => 7, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/magic-portfolio-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/magic-portfolio-light.png',
|
||||
|
|
@ -253,6 +283,8 @@ return [
|
|||
[
|
||||
'key' => 'littlelink',
|
||||
'name' => 'LittleLink',
|
||||
'tagline' => 'Personal website for creators to merge all URLs to social profiles.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/littlelink-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/littlelink-light.png',
|
||||
|
|
@ -270,6 +302,8 @@ return [
|
|||
[
|
||||
'key' => 'logspot',
|
||||
'name' => 'Logspot',
|
||||
'tagline' => 'Website to publish changelogs of your application.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::BLOG],
|
||||
'screenshotDark' => $url . '/images/sites/templates/logspot-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/logspot-light.png',
|
||||
|
|
@ -290,6 +324,8 @@ return [
|
|||
[
|
||||
'key' => 'astro-nano',
|
||||
'name' => 'Astro Nano',
|
||||
'tagline' => 'Minimal personal website to showcase your projects, articles, and more.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/astro-nano-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/astro-nano-light.png',
|
||||
|
|
@ -309,6 +345,8 @@ return [
|
|||
[
|
||||
'key' => 'astro-starlight',
|
||||
'name' => 'Astro Starlight',
|
||||
'tagline' => 'Platform for documentation and knowledge sharing powered by Astro.',
|
||||
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::DOCUMENTATION],
|
||||
'screenshotDark' => $url . '/images/sites/templates/astro-starlight-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/astro-starlight-light.png',
|
||||
|
|
@ -328,6 +366,8 @@ return [
|
|||
[
|
||||
'key' => 'astro-sphere',
|
||||
'name' => 'Astro Sphere',
|
||||
'tagline' => 'Modern personal website to showcase your projects, articles, and more.',
|
||||
'score' => 7, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/astro-sphere-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/astro-sphere-light.png',
|
||||
|
|
@ -347,6 +387,8 @@ return [
|
|||
[
|
||||
'key' => 'astro-starlog',
|
||||
'name' => 'Astro Starlog',
|
||||
'tagline' => 'Platform for publishing written content and media powered by Astro.',
|
||||
'score' => 5, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::BLOG],
|
||||
'screenshotDark' => $url . '/images/sites/templates/astro-starlog-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/astro-starlog-light.png',
|
||||
|
|
@ -366,6 +408,8 @@ return [
|
|||
[
|
||||
'key' => 'onelink',
|
||||
'name' => 'Onelink',
|
||||
'tagline' => 'Personal website for creators to merge all URLs to social profiles.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/onelink-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/onelink-light.png',
|
||||
|
|
@ -387,6 +431,8 @@ return [
|
|||
'key' => 'starter-for-flutter',
|
||||
'name' => 'Flutter starter',
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple Flutter application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-flutter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-flutter-light.png',
|
||||
'frameworks' => [
|
||||
|
|
@ -430,6 +476,8 @@ return [
|
|||
'key' => 'starter-for-js',
|
||||
'name' => 'JavaScript starter',
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple JavaScript application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-js-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-js-light.png',
|
||||
'frameworks' => [
|
||||
|
|
@ -472,6 +520,8 @@ return [
|
|||
'key' => 'starter-for-angular',
|
||||
'name' => 'Angular starter',
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple Angular application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-angular-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-angular-light.png',
|
||||
'frameworks' => [
|
||||
|
|
@ -516,6 +566,8 @@ return [
|
|||
'key' => 'starter-for-svelte',
|
||||
'name' => 'Svelte starter',
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple Svelte application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-svelte-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-svelte-light.png',
|
||||
'frameworks' => [
|
||||
|
|
@ -558,6 +610,8 @@ return [
|
|||
'key' => 'starter-for-react',
|
||||
'name' => 'React starter',
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple React application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-react-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-react-light.png',
|
||||
'frameworks' => [
|
||||
|
|
@ -600,6 +654,8 @@ return [
|
|||
'key' => 'starter-for-vue',
|
||||
'name' => 'Vue starter',
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple Vue application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-vue-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-vue-light.png',
|
||||
'frameworks' => [
|
||||
|
|
@ -642,6 +698,8 @@ return [
|
|||
'key' => 'starter-for-react-native',
|
||||
'name' => 'React Native starter',
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple React Native application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-react-native-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-react-native-light.png',
|
||||
'frameworks' => [
|
||||
|
|
@ -685,6 +743,8 @@ return [
|
|||
'key' => 'starter-for-nextjs',
|
||||
'name' => 'Next.js starter',
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple Next.js application integrated with Appwrite SDK.',
|
||||
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-nextjs-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-nextjs-light.png',
|
||||
'frameworks' => [
|
||||
|
|
@ -727,6 +787,8 @@ return [
|
|||
'key' => 'starter-for-nuxt',
|
||||
'name' => 'Nuxt starter',
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple Nuxt application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-nuxt-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-nuxt-light.png',
|
||||
'frameworks' => [
|
||||
|
|
@ -768,6 +830,8 @@ return [
|
|||
[
|
||||
'key' => 'template-for-event',
|
||||
'name' => 'Event template',
|
||||
'tagline' => 'Hackathon landing page with support for project submissions.',
|
||||
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::EVENTS],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-event-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-event-light.png',
|
||||
|
|
@ -804,6 +868,8 @@ return [
|
|||
[
|
||||
'key' => 'template-for-portfolio',
|
||||
'name' => 'Portfolio template',
|
||||
'tagline' => 'Simple personal website to showcase your projects, articles, and more.',
|
||||
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-portfolio-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-portfolio-light.png',
|
||||
|
|
@ -821,6 +887,8 @@ return [
|
|||
[
|
||||
'key' => 'template-for-store',
|
||||
'name' => 'Store template',
|
||||
'tagline' => 'E-commerce platform for selling products with Stripe integration.',
|
||||
'score' => 7, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::ECOMMERCE],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-store-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-store-light.png',
|
||||
|
|
@ -863,6 +931,8 @@ return [
|
|||
[
|
||||
'key' => 'template-for-blog',
|
||||
'name' => 'Blog template',
|
||||
'tagline' => 'Platform for publishing written content and media.',
|
||||
'score' => 7, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::BLOG],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-blog-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-blog-light.png',
|
||||
|
|
@ -880,6 +950,8 @@ return [
|
|||
[
|
||||
'key' => 'astro-starter',
|
||||
'name' => 'Astro starter',
|
||||
'tagline' => 'Sample application built with Astro, a content-driven web framework.',
|
||||
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'screenshotDark' => $url . '/images/sites/templates/astro-starter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/astro-starter-light.png',
|
||||
|
|
@ -897,6 +969,8 @@ return [
|
|||
[
|
||||
'key' => 'remix-starter',
|
||||
'name' => 'Remix starter',
|
||||
'tagline' => 'Sample application built with Remix, a React meta-framework.',
|
||||
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'screenshotDark' => $url . '/images/sites/templates/remix-starter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/remix-starter-light.png',
|
||||
|
|
|
|||
|
|
@ -269,7 +269,8 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $resource->getAttribute('runtime', '') . '" is not supported');
|
||||
}
|
||||
|
||||
if ($deployment->getAttribute('status') !== 'ready') {
|
||||
$allowAnyStatus = !\is_null($apiKey) && $apiKey->isDeploymentStatusIgnored();
|
||||
if (!$allowAnyStatus && $deployment->getAttribute('status') !== 'ready') {
|
||||
if ($deployment->getAttribute('status') === 'failed') {
|
||||
throw new AppwriteException(AppwriteException::BUILD_FAILED);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
<title>404 Not Found</title>
|
||||
|
||||
<style>
|
||||
@import url(https://fonts.bunny.net/css?family=fira-code:400|inter:400);
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
|
@ -36,7 +38,7 @@
|
|||
background: var(--color-overlay-on-neutral, rgba(0, 0, 0, 0.06));
|
||||
color: var(--color-fgColor-neutral-secondary, #56565C);
|
||||
text-align: center;
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-family: var(--font-family-sansSerif, Inter), sans-serif;
|
||||
font-size: var(--font-size-S, 14px);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
|
|
@ -47,7 +49,7 @@
|
|||
h1 {
|
||||
color: var(--color-fgColor-neutral-primary, #2D2D31);
|
||||
text-align: center;
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-family: var(--font-family-sansSerif, Inter), sans-serif;
|
||||
font-size: var(--font-size-XXXL, 32px);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
|
|
@ -59,7 +61,7 @@
|
|||
|
||||
button {
|
||||
border-radius: var(--border-radius-S, 8px);
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-family: var(--font-family-sansSerif, Inter), sans-serif;
|
||||
font-size: var(--font-size-S, 14px);
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
|
|
@ -88,7 +90,7 @@
|
|||
}
|
||||
|
||||
.brand p {
|
||||
font-family: var(--font-family-monospace, "Fira Code");
|
||||
font-family: var(--font-family-monospace, "Fira Code"), monospace;
|
||||
font-size: var(--font-size-XS, 12px);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
|
|
|
|||
40
composer.lock
generated
40
composer.lock
generated
|
|
@ -709,16 +709,16 @@
|
|||
},
|
||||
{
|
||||
"name": "google/protobuf",
|
||||
"version": "v4.30.1",
|
||||
"version": "v4.30.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/protocolbuffers/protobuf-php.git",
|
||||
"reference": "f29ba8a30dfd940efb3a8a75dc44446539101f24"
|
||||
"reference": "a4c4d8565b40b9f76debc9dfeb221412eacb8ced"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/f29ba8a30dfd940efb3a8a75dc44446539101f24",
|
||||
"reference": "f29ba8a30dfd940efb3a8a75dc44446539101f24",
|
||||
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/a4c4d8565b40b9f76debc9dfeb221412eacb8ced",
|
||||
"reference": "a4c4d8565b40b9f76debc9dfeb221412eacb8ced",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -747,9 +747,9 @@
|
|||
"proto"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.30.1"
|
||||
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.30.2"
|
||||
},
|
||||
"time": "2025-03-13T21:08:17+00:00"
|
||||
"time": "2025-03-26T18:01:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "jean85/pretty-package-versions",
|
||||
|
|
@ -4700,16 +4700,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/swoole",
|
||||
"version": "0.8.2",
|
||||
"version": "0.8.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/swoole.git",
|
||||
"reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4"
|
||||
"reference": "1af73dd3e73987cf729c7db399054e4a70befd99"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4",
|
||||
"reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4",
|
||||
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/1af73dd3e73987cf729c7db399054e4a70befd99",
|
||||
"reference": "1af73dd3e73987cf729c7db399054e4a70befd99",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4745,9 +4745,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/swoole/issues",
|
||||
"source": "https://github.com/utopia-php/swoole/tree/0.8.2"
|
||||
"source": "https://github.com/utopia-php/swoole/tree/0.8.3"
|
||||
},
|
||||
"time": "2024-02-01T14:54:12+00:00"
|
||||
"time": "2025-03-26T10:09:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/system",
|
||||
|
|
@ -5089,16 +5089,16 @@
|
|||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "0.40.10",
|
||||
"version": "0.40.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "054ac96285caf4f77879087b2416a5ddb8263051"
|
||||
"reference": "0ec5f4a60c15e33e208bc3444ba6148b1d0f0027"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/054ac96285caf4f77879087b2416a5ddb8263051",
|
||||
"reference": "054ac96285caf4f77879087b2416a5ddb8263051",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/0ec5f4a60c15e33e208bc3444ba6148b1d0f0027",
|
||||
"reference": "0ec5f4a60c15e33e208bc3444ba6148b1d0f0027",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -5134,9 +5134,9 @@
|
|||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.40.10"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.40.11"
|
||||
},
|
||||
"time": "2025-03-25T13:44:16+00:00"
|
||||
"time": "2025-03-26T10:53:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
|
|
@ -8507,7 +8507,7 @@
|
|||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
|
@ -8531,5 +8531,5 @@
|
|||
"platform-overrides": {
|
||||
"php": "8.3"
|
||||
},
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
|
|
|||
BIN
public/images/sites/templates/lynx-starter-dark.png
Normal file
BIN
public/images/sites/templates/lynx-starter-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 324 KiB |
BIN
public/images/sites/templates/lynx-starter-light.png
Normal file
BIN
public/images/sites/templates/lynx-starter-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 323 KiB |
|
|
@ -24,6 +24,7 @@ class Key
|
|||
protected bool $bannerDisabled = false,
|
||||
protected bool $projectCheckDisabled = false,
|
||||
protected bool $previewAuthDisabled = false,
|
||||
protected bool $deploymentStatusIgnored = false,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -79,6 +80,11 @@ class Key
|
|||
return $this->previewAuthDisabled;
|
||||
}
|
||||
|
||||
public function isDeploymentStatusIgnored(): bool
|
||||
{
|
||||
return $this->deploymentStatusIgnored;
|
||||
}
|
||||
|
||||
public function isProjectCheckDisabled(): bool
|
||||
{
|
||||
return $this->projectCheckDisabled;
|
||||
|
|
@ -139,6 +145,7 @@ class Key
|
|||
$bannerDisabled = $payload['bannerDisabled'] ?? false;
|
||||
$projectCheckDisabled = $payload['projectCheckDisabled'] ?? false;
|
||||
$previewAuthDisabled = $payload['previewAuthDisabled'] ?? false;
|
||||
$deploymentStatusIgnored = $payload['deploymentStatusIgnored'] ?? false;
|
||||
$scopes = \array_merge($payload['scopes'] ?? [], $scopes);
|
||||
|
||||
if (!$projectCheckDisabled && $projectId !== $project->getId()) {
|
||||
|
|
@ -156,7 +163,8 @@ class Key
|
|||
$hostnameOverride,
|
||||
$bannerDisabled,
|
||||
$projectCheckDisabled,
|
||||
$previewAuthDisabled
|
||||
$previewAuthDisabled,
|
||||
$deploymentStatusIgnored
|
||||
);
|
||||
case API_KEY_STANDARD:
|
||||
$key = $project->find(
|
||||
|
|
|
|||
|
|
@ -71,10 +71,14 @@ class XList extends Base
|
|||
});
|
||||
}
|
||||
|
||||
$responseTemplates = \array_slice($templates, $offset, $limit);
|
||||
\usort($templates, function ($a, $b) {
|
||||
return $b['score'] <=> $a['score'];
|
||||
});
|
||||
|
||||
$templates = \array_slice($templates, $offset, $limit);
|
||||
$response->dynamic(new Document([
|
||||
'templates' => $responseTemplates,
|
||||
'total' => \count($responseTemplates),
|
||||
'templates' => $templates,
|
||||
'total' => \count($templates),
|
||||
]), Response::MODEL_TEMPLATE_FUNCTION_LIST);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ use Utopia\Storage\Device\Local;
|
|||
use Utopia\System\System;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
||||
use function Swoole\Coroutine\batch;
|
||||
|
||||
class Builds extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
|
|
@ -783,7 +785,6 @@ class Builds extends Action
|
|||
$deployment->setAttribute('buildStartAt', DateTime::format((new \DateTime())->setTimestamp(floor($response['startTime']))));
|
||||
$deployment->setAttribute('buildEndAt', $endTime);
|
||||
$deployment->setAttribute('buildDuration', \intval(\ceil($durationEnd - $durationStart)));
|
||||
$deployment->setAttribute('status', 'ready');
|
||||
$deployment->setAttribute('buildPath', $response['path']);
|
||||
$deployment->setAttribute('buildSize', $response['size']);
|
||||
$deployment->setAttribute('totalSize', $deployment->getAttribute('buildSize', 0) + $deployment->getAttribute('sourceSize', 0));
|
||||
|
|
@ -792,25 +793,16 @@ class Builds extends Action
|
|||
foreach ($response['output'] as $log) {
|
||||
$logs .= $log['content'];
|
||||
}
|
||||
$logs .= "[37mCapturing screenshots ...[0m\n";
|
||||
$deployment->setAttribute('buildLogs', $logs);
|
||||
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
||||
|
||||
if ($deployment->getInternalId() === $resource->getAttribute('latestDeploymentInternalId', '')) {
|
||||
$resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource);
|
||||
}
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
|
||||
$queueForRealtime
|
||||
->setPayload($deployment->getArrayCopy())
|
||||
->trigger();
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForPlatform);
|
||||
}
|
||||
|
||||
Console::success("Build id: $deploymentId created");
|
||||
|
||||
/** Screenshot site */
|
||||
if ($resource->getCollection() === 'sites') {
|
||||
try {
|
||||
$rule = Authorization::skip(fn () => $dbForPlatform->findOne('rules', [
|
||||
|
|
@ -848,72 +840,89 @@ class Builds extends Action
|
|||
'bannerDisabled' => true,
|
||||
'projectCheckDisabled' => true,
|
||||
'previewAuthDisabled' => true,
|
||||
'deploymentStatusIgnored' => true
|
||||
]);
|
||||
|
||||
// TODO: @Meldiron if becomes too slow, do concurrently
|
||||
foreach ($configs as $key => $config) {
|
||||
$config['headers'] = \array_merge($config['headers'] ?? [], [
|
||||
'x-appwrite-key' => API_KEY_DYNAMIC . '_' . $apiKey
|
||||
]);
|
||||
$screenshotError = null;
|
||||
$screenshots = batch(\array_map(function ($key) use ($configs, $deviceForFiles, $apiKey, $resource, $client, $bucket, $project, $dbForPlatform, &$screenshotError) {
|
||||
return function () use ($key, $configs, $deviceForFiles, $apiKey, $resource, $client, $bucket, $project, $dbForPlatform, &$screenshotError) {
|
||||
try {
|
||||
$config = $configs[$key];
|
||||
|
||||
$config['sleep'] = 3000;
|
||||
$config['headers'] = \array_merge($config['headers'] ?? [], [
|
||||
'x-appwrite-key' => API_KEY_DYNAMIC . '_' . $apiKey
|
||||
]);
|
||||
$config['sleep'] = 3000;
|
||||
|
||||
$frameworks = Config::getParam('frameworks', []);
|
||||
$framework = $frameworks[$resource->getAttribute('framework', '')] ?? null;
|
||||
if (!is_null($framework)) {
|
||||
$config['sleep'] = $framework['screenshotSleep'];
|
||||
}
|
||||
$frameworks = Config::getParam('frameworks', []);
|
||||
$framework = $frameworks[$resource->getAttribute('framework', '')] ?? null;
|
||||
if (!is_null($framework)) {
|
||||
$config['sleep'] = $framework['screenshotSleep'];
|
||||
}
|
||||
|
||||
$response = $client->fetch(
|
||||
url: 'http://appwrite-browser:3000/v1/screenshots',
|
||||
method: 'POST',
|
||||
body: $config
|
||||
);
|
||||
$fetchResponse = $client->fetch(
|
||||
url: 'http://appwrite-browser:3000/v1/screenshots',
|
||||
method: 'POST',
|
||||
body: $config
|
||||
);
|
||||
|
||||
if ($response->getStatusCode() >= 400) {
|
||||
throw new \Exception($response->getBody());
|
||||
}
|
||||
if ($fetchResponse->getStatusCode() >= 400) {
|
||||
throw new \Exception($fetchResponse->getBody());
|
||||
}
|
||||
|
||||
$screenshot = $response->getBody();
|
||||
$screenshot = $fetchResponse->getBody();
|
||||
|
||||
$fileId = ID::unique();
|
||||
$fileName = $fileId . '.png';
|
||||
$path = $deviceForFiles->getPath($fileName);
|
||||
$path = str_ireplace($deviceForFiles->getRoot(), $deviceForFiles->getRoot() . DIRECTORY_SEPARATOR . $bucket->getId(), $path); // Add bucket id to path after root
|
||||
$success = $deviceForFiles->write($path, $screenshot, "image/png");
|
||||
$fileId = ID::unique();
|
||||
$fileName = $fileId . '.png';
|
||||
$path = $deviceForFiles->getPath($fileName);
|
||||
$path = str_ireplace($deviceForFiles->getRoot(), $deviceForFiles->getRoot() . DIRECTORY_SEPARATOR . $bucket->getId(), $path); // Add bucket id to path after root
|
||||
$success = $deviceForFiles->write($path, $screenshot, "image/png");
|
||||
|
||||
if (!$success) {
|
||||
throw new \Exception("Screenshot failed to save");
|
||||
}
|
||||
if (!$success) {
|
||||
throw new \Exception("Screenshot failed to save");
|
||||
}
|
||||
|
||||
$teamId = $project->getAttribute('teamId', '');
|
||||
$file = new Document([
|
||||
'$id' => $fileId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
],
|
||||
'bucketId' => $bucket->getId(),
|
||||
'bucketInternalId' => $bucket->getInternalId(),
|
||||
'name' => $fileName,
|
||||
'path' => $path,
|
||||
'signature' => $deviceForFiles->getFileHash($path),
|
||||
'mimeType' => $deviceForFiles->getFileMimeType($path),
|
||||
'sizeOriginal' => \strlen($screenshot),
|
||||
'sizeActual' => $deviceForFiles->getFileSize($path),
|
||||
'algorithm' => Compression::GZIP,
|
||||
'comment' => '',
|
||||
'chunksTotal' => 1,
|
||||
'chunksUploaded' => 1,
|
||||
'openSSLVersion' => null,
|
||||
'openSSLCipher' => null,
|
||||
'openSSLTag' => null,
|
||||
'openSSLIV' => null,
|
||||
'search' => implode(' ', [$fileId, $fileName]),
|
||||
'metadata' => ['content_type' => $deviceForFiles->getFileMimeType($path)],
|
||||
]);
|
||||
$file = Authorization::skip(fn () => $dbForPlatform->createDocument('bucket_' . $bucket->getInternalId(), $file));
|
||||
$teamId = $project->getAttribute('teamId', '');
|
||||
$file = new Document([
|
||||
'$id' => $fileId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
],
|
||||
'bucketId' => $bucket->getId(),
|
||||
'bucketInternalId' => $bucket->getInternalId(),
|
||||
'name' => $fileName,
|
||||
'path' => $path,
|
||||
'signature' => $deviceForFiles->getFileHash($path),
|
||||
'mimeType' => $deviceForFiles->getFileMimeType($path),
|
||||
'sizeOriginal' => \strlen($screenshot),
|
||||
'sizeActual' => $deviceForFiles->getFileSize($path),
|
||||
'algorithm' => Compression::GZIP,
|
||||
'comment' => '',
|
||||
'chunksTotal' => 1,
|
||||
'chunksUploaded' => 1,
|
||||
'openSSLVersion' => null,
|
||||
'openSSLCipher' => null,
|
||||
'openSSLTag' => null,
|
||||
'openSSLIV' => null,
|
||||
'search' => implode(' ', [$fileId, $fileName]),
|
||||
'metadata' => ['content_type' => $deviceForFiles->getFileMimeType($path)],
|
||||
]);
|
||||
$file = Authorization::skip(fn () => $dbForPlatform->createDocument('bucket_' . $bucket->getInternalId(), $file));
|
||||
|
||||
$deployment->setAttribute($key, $fileId);
|
||||
return [ 'key' => $key, 'fileId' => $fileId ];
|
||||
} catch (\Throwable $th) {
|
||||
$screenshotError = $th->getMessage();
|
||||
return;
|
||||
}
|
||||
};
|
||||
}, \array_keys($configs)));
|
||||
|
||||
if (!\is_null($screenshotError)) {
|
||||
throw new \Exception($screenshotError);
|
||||
}
|
||||
|
||||
foreach ($screenshots as $screenshot) {
|
||||
$deployment->setAttribute($screenshot['key'], $screenshot['fileId']);
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
|
|
@ -928,6 +937,25 @@ class Builds extends Action
|
|||
}
|
||||
}
|
||||
|
||||
/** Update the status */
|
||||
$deployment->setAttribute('status', 'ready');
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
||||
|
||||
if ($deployment->getInternalId() === $resource->getAttribute('latestDeploymentInternalId', '')) {
|
||||
$resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource);
|
||||
}
|
||||
|
||||
$queueForRealtime
|
||||
->setPayload($deployment->getArrayCopy())
|
||||
->trigger();
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForPlatform);
|
||||
}
|
||||
|
||||
Console::success("Build id: $deploymentId created");
|
||||
|
||||
/** Set auto deploy */
|
||||
if ($deployment->getAttribute('activate') === true) {
|
||||
$resource->setAttribute('live', true);
|
||||
|
|
@ -1066,6 +1094,12 @@ class Builds extends Action
|
|||
Authorization::skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning('Build failed:');
|
||||
Console::error($th->getMessage());
|
||||
Console::error($th->getFile());
|
||||
Console::error($th->getLine());
|
||||
Console::error($th->getTraceAsString());
|
||||
|
||||
if ($dbForProject->getDocument('deployments', $deploymentId)->getAttribute('status') === 'canceled') {
|
||||
Console::info('Build has been canceled');
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class Get extends Base
|
|||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/sites/templates/:templateId')
|
||||
->desc('Get site template')
|
||||
->groups(['api', 'sites'])
|
||||
->groups(['api'])
|
||||
->label('scope', 'public')
|
||||
->label('resourceType', RESOURCE_TYPE_SITES)
|
||||
->label('sdk', new Method(
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class XList extends Base
|
|||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/sites/templates')
|
||||
->desc('List templates')
|
||||
->groups(['api', 'sites'])
|
||||
->groups(['api'])
|
||||
->label('scope', 'public')
|
||||
->label('resourceType', RESOURCE_TYPE_SITES)
|
||||
->label('sdk', new Method(
|
||||
|
|
@ -76,10 +76,15 @@ class XList extends Base
|
|||
});
|
||||
}
|
||||
|
||||
$responseTemplates = \array_slice($templates, $offset, $limit);
|
||||
\usort($templates, function ($a, $b) {
|
||||
return $b['score'] <=> $a['score'];
|
||||
});
|
||||
|
||||
$templates = \array_slice($templates, $offset, $limit);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'templates' => $responseTemplates,
|
||||
'total' => \count($responseTemplates),
|
||||
'templates' => $templates,
|
||||
'total' => \count($templates),
|
||||
]), Response::MODEL_TEMPLATE_SITE_LIST);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ class Preview extends Adapter
|
|||
|
||||
$banner = <<<EOT
|
||||
<style>
|
||||
@import url(https://fonts.bunny.net/css?family=fira-code:400|inter:400);
|
||||
|
||||
#appwrite-preview {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
|
@ -80,7 +82,7 @@ class Preview extends Adapter
|
|||
padding: 0;
|
||||
margin: 0;
|
||||
color: var(--color-fgColor-neutral-secondary, #56565C);
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-family: var(--font-family-sansSerif, Inter), sans-serif;
|
||||
font-size: var(--font-size-XS, 12px);
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
|
|
@ -97,7 +99,7 @@ class Preview extends Adapter
|
|||
padding: var(--space-1, 2px) var(--space-2, 4px);
|
||||
color: var(--color-fgColor-neutral-secondary, #56565C);
|
||||
text-align: center;
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-family: var(--font-family-sansSerif, Inter), sans-serif;
|
||||
font-size: var(--font-size-XS, 12px);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
|
|
@ -120,7 +122,7 @@ class Preview extends Adapter
|
|||
|
||||
#appwrite-preview-text {
|
||||
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-family: var(--font-family-sansSerif, Inter), sans-serif;
|
||||
font-size: var(--font-size-XS, 12px);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,12 @@ class TemplateSite extends Model
|
|||
'default' => '',
|
||||
'example' => 'Starter site',
|
||||
])
|
||||
->addRule('tagline', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Short description of template',
|
||||
'default' => '',
|
||||
'example' => 'Minimal web app integrating with Appwrite.',
|
||||
])
|
||||
->addRule('demoUrl', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'URL hosting a template demo.',
|
||||
|
|
|
|||
|
|
@ -614,19 +614,26 @@ class RealtimeConsoleClientTest extends Scope
|
|||
|
||||
$previousBuildLogs = $response['data']['payload']['buildLogs'];
|
||||
|
||||
$this->assertThat(
|
||||
$response['data']['payload']['status'],
|
||||
$this->logicalOr(
|
||||
$this->equalTo('building'),
|
||||
$this->equalTo('ready'),
|
||||
),
|
||||
);
|
||||
$this->assertEquals('building', $response['data']['payload']['status']);
|
||||
|
||||
if ($response['data']['payload']['status'] === 'ready') {
|
||||
if (!empty($response['data']['payload']['buildEndAt'])) {
|
||||
$this->assertNotEmpty($response['data']['payload']['buildEndAt']);
|
||||
$this->assertNotEmpty($response['data']['payload']['buildStartAt']);
|
||||
$this->assertNotEmpty($response['data']['payload']['buildDuration']);
|
||||
$this->assertNotEmpty($response['data']['payload']['buildPath']);
|
||||
$this->assertNotEmpty($response['data']['payload']['buildSize']);
|
||||
$this->assertNotEmpty($response['data']['payload']['totalSize']);
|
||||
$this->assertNotEmpty($response['data']['payload']['buildLogs']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
$this->assertContains("functions.{$functionId}.deployments.{$deploymentId}.update", $response['data']['events']);
|
||||
$this->assertContains('console', $response['data']['channels']);
|
||||
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
|
||||
$this->assertEquals("ready", $response['data']['payload']['status']);
|
||||
|
||||
$client->close();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class SitesCustomClientTest extends Scope
|
|||
$this->assertArrayHasKey('variables', $template);
|
||||
$this->assertArrayHasKey('screenshotDark', $template);
|
||||
$this->assertArrayHasKey('screenshotLight', $template);
|
||||
$this->assertArrayHasKey('tagline', $template);
|
||||
}
|
||||
|
||||
// List templates with pagination
|
||||
|
|
@ -126,6 +127,7 @@ class SitesCustomClientTest extends Scope
|
|||
$this->assertEquals('React starter', $template['body']['name']);
|
||||
$this->assertEquals(['starter'], $template['body']['useCases']);
|
||||
$this->assertEquals('github', $template['body']['vcsProvider']);
|
||||
$this->assertEquals('Simple React application integrated with Appwrite SDK.', $template['body']['tagline']);
|
||||
$this->assertIsArray($template['body']['frameworks']);
|
||||
$this->assertEquals('http://localhost/images/sites/templates/starter-for-react-dark.png', $template['body']['screenshotDark']);
|
||||
$this->assertEquals('http://localhost/images/sites/templates/starter-for-react-light.png', $template['body']['screenshotLight']);
|
||||
|
|
|
|||
|
|
@ -2430,4 +2430,38 @@ class SitesCustomServerTest extends Scope
|
|||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testPermanentRedirect(): void
|
||||
{
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Sub project site',
|
||||
'framework' => 'other',
|
||||
'buildRuntime' => 'node-22',
|
||||
'outputDirectory' => './'
|
||||
]);
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
$domain = $this->setupSiteDomain($siteId);
|
||||
$this->assertNotEmpty($domain);
|
||||
|
||||
$deploymentId = $this->setupDeployment($siteId, [
|
||||
'code' => $this->packageSite('sub-directories'),
|
||||
'activate' => 'true'
|
||||
]);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/');
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString('Sub-directory index', $response['body']);
|
||||
$response1 = $proxyClient->call(Client::METHOD_GET, '/project1');
|
||||
$this->assertEquals(200, $response1['headers']['status-code']);
|
||||
$this->assertStringContainsString('Sub-directory project1', $response1['body']);
|
||||
$response2 = $proxyClient->call(Client::METHOD_GET, '/project1/');
|
||||
$this->assertEquals(200, $response2['headers']['status-code']);
|
||||
$this->assertStringContainsString('Sub-directory project1', $response2['body']);
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
5
tests/resources/sites/sub-directories/index.html
Normal file
5
tests/resources/sites/sub-directories/index.html
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<html lang="en">
|
||||
<body>
|
||||
<p>Sub-directory index</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<html lang="en">
|
||||
<body>
|
||||
<p>Sub-directory project1</p>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in a new issue