mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch 'feat-sites' into feat-zip-deployment
This commit is contained in:
@@ -1185,6 +1185,39 @@ return [
|
||||
'default' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('personalAccessToken'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 256,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['encrypt'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('personalAccessTokenExpiry'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('personalRefreshToken'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 256,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['encrypt'],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
|
||||
|
||||
@@ -375,6 +375,13 @@ return [
|
||||
'code' => 409,
|
||||
],
|
||||
|
||||
/** Console */
|
||||
Exception::RESOURCE_ALREADY_EXISTS => [
|
||||
'name' => Exception::RESOURCE_ALREADY_EXISTS,
|
||||
'description' => 'Resource with the requested ID already exists. Please choose a different ID and try again.',
|
||||
'code' => 409,
|
||||
],
|
||||
|
||||
/** Membership */
|
||||
Exception::MEMBERSHIP_NOT_FOUND => [
|
||||
'name' => Exception::MEMBERSHIP_NOT_FOUND,
|
||||
@@ -868,6 +875,11 @@ return [
|
||||
'description' => 'Variable with the same ID already exists in this project. Try again with a different ID.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::VARIABLE_CANNOT_UNSET_SECRET => [
|
||||
'name' => Exception::VARIABLE_CANNOT_UNSET_SECRET,
|
||||
'description' => 'Secret variables cannot be marked as non-secret. Please re-create the variable if this is your intention.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::GRAPHQL_NO_QUERY => [
|
||||
'name' => Exception::GRAPHQL_NO_QUERY,
|
||||
'description' => 'Param "query" is not optional.',
|
||||
|
||||
@@ -26,6 +26,7 @@ $member = [
|
||||
'subscribers.write',
|
||||
'subscribers.read',
|
||||
'assistant.read',
|
||||
'rules.read'
|
||||
];
|
||||
|
||||
$admins = [
|
||||
|
||||
@@ -127,6 +127,56 @@ return [
|
||||
'providerVersion' => '0.2.*',
|
||||
'variables' => [],
|
||||
],
|
||||
[
|
||||
'key' => 'sveltekit-with-appwrite',
|
||||
'name' => 'SvelteKit with Appwrite',
|
||||
'useCases' => ['starter'],
|
||||
'demoUrl' => '',
|
||||
'demoImage' => '',
|
||||
'frameworks' => [
|
||||
getFramework('SVELTEKIT', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'starter-for-svelte',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server.',
|
||||
'value' => 'https://cloud.appwrite.io/v1',
|
||||
'placeholder' => 'https://cloud.appwrite.io/v1',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID.',
|
||||
'value' => '',
|
||||
'placeholder' => '67ab5b6d000db4f5f59d',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_PROJECT_NAME',
|
||||
'description' => 'Your Appwrite project name.',
|
||||
'value' => '',
|
||||
'placeholder' => 'My project',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_VERSION',
|
||||
'description' => 'Version of Appwrite server.',
|
||||
'value' => '1.7.0',
|
||||
'placeholder' => '1.7.0',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'key' => 'astro-starter',
|
||||
'name' => 'Astro starter website',
|
||||
|
||||
@@ -7,4 +7,5 @@ return [
|
||||
"gif" => "image/gif",
|
||||
"png" => "image/png",
|
||||
"heic" => "image/heic",
|
||||
"webp" => "image/webp",
|
||||
];
|
||||
|
||||
@@ -1585,7 +1585,7 @@ App::post('/v1/functions/:functionId/variables')
|
||||
->param('functionId', '', new UID(), 'Function unique ID.', false)
|
||||
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
|
||||
->param('secret', false, new Boolean(), 'Is secret? Secret variables can only be updated or deleted, they cannot be read.', true)
|
||||
->param('secret', true, new Boolean(), 'Secret variables can be updated or deleted, but only functions can read them during build and runtime.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
@@ -1729,10 +1729,11 @@ App::put('/v1/functions/:functionId/variables/:variableId')
|
||||
->param('variableId', '', new UID(), 'Variable unique ID.', false)
|
||||
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', true)
|
||||
->param('secret', null, new Boolean(), 'Secret variables can be updated or deleted, but only functions can read them during build and runtime.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForPlatform) {
|
||||
->action(function (string $functionId, string $variableId, string $key, ?string $value, ?bool $secret, Response $response, Database $dbForProject, Database $dbForPlatform) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
@@ -1749,9 +1750,14 @@ App::put('/v1/functions/:functionId/variables/:variableId')
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($variable->getAttribute('secret') === true && $secret === false) {
|
||||
throw new Exception(Exception::VARIABLE_CANNOT_UNSET_SECRET);
|
||||
}
|
||||
|
||||
$variable
|
||||
->setAttribute('key', $key)
|
||||
->setAttribute('value', $value ?? $variable->getAttribute('value'))
|
||||
->setAttribute('secret', $secret ?? $variable->getAttribute('secret'))
|
||||
->setAttribute('search', implode(' ', [$variableId, $function->getId(), $key, 'function']));
|
||||
|
||||
try {
|
||||
|
||||
@@ -388,7 +388,7 @@ App::post('/v1/project/variables')
|
||||
))
|
||||
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
|
||||
->param('secret', false, new Boolean(), 'Is secret? Secret variables can only be updated or deleted, they cannot be read.', true)
|
||||
->param('secret', true, new Boolean(), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true)
|
||||
->inject('project')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
@@ -509,19 +509,25 @@ App::put('/v1/project/variables/:variableId')
|
||||
->param('variableId', '', new UID(), 'Variable unique ID.', false)
|
||||
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', true)
|
||||
->param('secret', null, new Boolean(), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true)
|
||||
->inject('project')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $variableId, string $key, ?string $value, Document $project, Response $response, Database $dbForProject, Database $dbForPlatform) {
|
||||
->action(function (string $variableId, string $key, ?string $value, ?bool $secret, Document $project, Response $response, Database $dbForProject, Database $dbForPlatform) {
|
||||
$variable = $dbForProject->getDocument('variables', $variableId);
|
||||
if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceType') !== 'project') {
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($variable->getAttribute('secret') === true && $secret === false) {
|
||||
throw new Exception(Exception::VARIABLE_CANNOT_UNSET_SECRET);
|
||||
}
|
||||
|
||||
$variable
|
||||
->setAttribute('key', $key)
|
||||
->setAttribute('value', $value ?? $variable->getAttribute('value'))
|
||||
->setAttribute('secret', $secret ?? $variable->getAttribute('secret'))
|
||||
->setAttribute('search', implode(' ', [$variableId, $key, 'project']));
|
||||
|
||||
try {
|
||||
|
||||
@@ -130,7 +130,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
};
|
||||
}
|
||||
|
||||
if ($type === 'function' || $type === 'site') {
|
||||
if ($type === 'function' || $type === 'site' || $type === 'deployment') {
|
||||
$method = $utopia->getRoute()?->getLabel('sdk', null);
|
||||
|
||||
if (empty($method)) {
|
||||
|
||||
Generated
+1
-1
@@ -8780,5 +8780,5 @@
|
||||
"platform-overrides": {
|
||||
"php": "8.3"
|
||||
},
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
||||
+1
-1
@@ -204,7 +204,7 @@ services:
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: appwrite/console:5.3.0-sites-rc.4
|
||||
image: appwrite/console:5.3.0-sites-rc.5
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
|
||||
@@ -118,6 +118,9 @@ class Exception extends \Exception
|
||||
public const TEAM_INVITE_MISMATCH = 'team_invite_mismatch';
|
||||
public const TEAM_ALREADY_EXISTS = 'team_already_exists';
|
||||
|
||||
/** Console */
|
||||
public const RESOURCE_ALREADY_EXISTS = 'resource_already_exists';
|
||||
|
||||
/** Membership */
|
||||
public const MEMBERSHIP_NOT_FOUND = 'membership_not_found';
|
||||
public const MEMBERSHIP_ALREADY_CONFIRMED = 'membership_already_confirmed';
|
||||
@@ -255,6 +258,7 @@ class Exception extends \Exception
|
||||
/** Variables */
|
||||
public const VARIABLE_NOT_FOUND = 'variable_not_found';
|
||||
public const VARIABLE_ALREADY_EXISTS = 'variable_already_exists';
|
||||
public const VARIABLE_CANNOT_UNSET_SECRET = 'variable_cannot_unset_secret';
|
||||
|
||||
/** Platform */
|
||||
public const PLATFORM_NOT_FOUND = 'platform_not_found';
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Appwrite\Platform;
|
||||
|
||||
use Appwrite\Platform\Modules\Console;
|
||||
use Appwrite\Platform\Modules\Core;
|
||||
use Appwrite\Platform\Modules\Functions;
|
||||
use Appwrite\Platform\Modules\Sites;
|
||||
@@ -14,5 +15,6 @@ class Appwrite extends Platform
|
||||
parent::__construct(new Core());
|
||||
$this->addModule(new Functions\Module());
|
||||
$this->addModule(new Sites\Module());
|
||||
$this->addModule(new Console\Module());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Resources;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Domain;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
class Get extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'getResources';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/console/resources')
|
||||
->desc('Check resource ID availability')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'rules.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'console',
|
||||
name: 'getResource',
|
||||
description: <<<EOT
|
||||
Check if a resource ID is available.
|
||||
EOT,
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_NOCONTENT,
|
||||
model: Response::MODEL_NONE,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::NONE,
|
||||
))
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'userId:{userId}, url:{url}')
|
||||
->label('abuse-time', 60)
|
||||
->param('value', '', new Text(256), 'Resource value.')
|
||||
->param('type', '', new WhiteList(['rules']), 'Resource type.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->callback([$this, 'action']);
|
||||
}
|
||||
|
||||
public function action(string $value, string $type, Response $response, Database $dbForPlatform)
|
||||
{
|
||||
if ($type === 'rules') {
|
||||
$validator = new Domain($value);
|
||||
|
||||
if (!$validator->isValid($value)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$document = Authorization::skip(fn () => $dbForPlatform->findOne('rules', [
|
||||
Query::equal('domain', [$value]),
|
||||
]));
|
||||
|
||||
if (!$document->isEmpty()) {
|
||||
throw new Exception(Exception::RESOURCE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$response->noContent();
|
||||
}
|
||||
|
||||
// Only occurs if type is added into whitelist, but not supported in action
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid type');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console;
|
||||
|
||||
use Appwrite\Platform\Modules\Console\Services\Http;
|
||||
use Utopia\Platform;
|
||||
|
||||
class Module extends Platform\Module
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->addService('http', new Http());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Services;
|
||||
|
||||
use Appwrite\Platform\Modules\Console\Http\Resources\Get as GetResourceAvailability;
|
||||
use Utopia\Platform\Service;
|
||||
|
||||
class Http extends Service
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->type = Service::TYPE_HTTP;
|
||||
// Resources
|
||||
$this->addAction(GetResourceAvailability::getName(), new GetResourceAvailability());
|
||||
}
|
||||
}
|
||||
@@ -56,14 +56,13 @@ class Create extends Base
|
||||
->param('siteId', '', new UID(), 'Site unique ID.', false)
|
||||
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
|
||||
->param('secret', false, new Boolean(), 'Is secret? Secret variables can only be updated or deleted, they cannot be read.', true)
|
||||
->param('secret', true, new Boolean(), 'Secret variables can be updated or deleted, but only sites can read them during build and runtime.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->callback([$this, 'action']);
|
||||
}
|
||||
|
||||
public function action(string $siteId, string $key, string $value, bool $secret, Response $response, Database $dbForProject, Database $dbForPlatform)
|
||||
public function action(string $siteId, string $key, string $value, bool $secret, Response $response, Database $dbForProject)
|
||||
{
|
||||
$site = $dbForProject->getDocument('sites', $siteId);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Update extends Base
|
||||
@@ -52,12 +53,13 @@ class Update extends Base
|
||||
->param('variableId', '', new UID(), 'Variable unique ID.', false)
|
||||
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', true)
|
||||
->param('secret', null, new Boolean(), 'Secret variables can be updated or deleted, but only sites can read them during build and runtime.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->callback([$this, 'action']);
|
||||
}
|
||||
|
||||
public function action(string $siteId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject)
|
||||
public function action(string $siteId, string $variableId, string $key, ?string $value, ?bool $secret, Response $response, Database $dbForProject)
|
||||
{
|
||||
$site = $dbForProject->getDocument('sites', $siteId);
|
||||
|
||||
@@ -74,9 +76,14 @@ class Update extends Base
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($variable->getAttribute('secret') === true && $secret === false) {
|
||||
throw new Exception(Exception::VARIABLE_CANNOT_UNSET_SECRET);
|
||||
}
|
||||
|
||||
$variable
|
||||
->setAttribute('key', $key)
|
||||
->setAttribute('value', $value ?? $variable->getAttribute('value'))
|
||||
->setAttribute('secret', $secret ?? $variable->getAttribute('secret'))
|
||||
->setAttribute('search', implode(' ', [$variableId, $site->getId(), $key, 'site']));
|
||||
|
||||
try {
|
||||
|
||||
@@ -82,6 +82,46 @@ trait FunctionsBase
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function getVariable(string $functionId, string $variableId): mixed
|
||||
{
|
||||
$variable = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/variables/' . $variableId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function updateVariable(string $functionId, string $variableId, mixed $params): mixed
|
||||
{
|
||||
$variable = $this->client->call(Client::METHOD_PUT, '/functions/' . $functionId . '/variables/' . $variableId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $params);
|
||||
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function listVariables(string $functionId, mixed $params = []): mixed
|
||||
{
|
||||
$variables = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/variables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $params);
|
||||
|
||||
return $variables;
|
||||
}
|
||||
|
||||
protected function deleteVariable(string $functionId, string $variableId): mixed
|
||||
{
|
||||
$variable = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId . '/variables/' . $variableId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function getFunction(string $functionId): mixed
|
||||
{
|
||||
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId, array_merge([
|
||||
|
||||
@@ -110,7 +110,8 @@ class FunctionsConsoleClientTest extends Scope
|
||||
$data['functionId'],
|
||||
[
|
||||
'key' => 'APP_TEST',
|
||||
'value' => 'TESTINGVALUE'
|
||||
'value' => 'TESTINGVALUE',
|
||||
'secret' => false
|
||||
]
|
||||
);
|
||||
|
||||
@@ -131,6 +132,7 @@ class FunctionsConsoleClientTest extends Scope
|
||||
$this->assertEquals(201, $variable['headers']['status-code']);
|
||||
$this->assertEquals('APP_TEST_1', $variable['body']['key']);
|
||||
$this->assertEmpty($variable['body']['value']);
|
||||
$this->assertTrue($variable['body']['secret']);
|
||||
|
||||
$secretVariableId = $variable['body']['$id'];
|
||||
|
||||
@@ -142,7 +144,8 @@ class FunctionsConsoleClientTest extends Scope
|
||||
$data['functionId'],
|
||||
[
|
||||
'key' => 'APP_TEST',
|
||||
'value' => 'ANOTHERTESTINGVALUE'
|
||||
'value' => 'ANOTHERTESTINGVALUE',
|
||||
'secret' => false
|
||||
]
|
||||
);
|
||||
|
||||
@@ -320,6 +323,41 @@ class FunctionsConsoleClientTest extends Scope
|
||||
$this->assertEquals("APP_TEST_UPDATE_2", $variable['body']['key']);
|
||||
$this->assertEquals("TESTINGVALUEUPDATED", $variable['body']['value']);
|
||||
|
||||
// convert non-secret variable to secret
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'APP_TEST_UPDATE_2',
|
||||
'secret' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals("APP_TEST_UPDATE_2", $response['body']['key']);
|
||||
$this->assertEmpty($response['body']['value']);
|
||||
$this->assertTrue($response['body']['secret']);
|
||||
|
||||
$variable = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $variable['headers']['status-code']);
|
||||
$this->assertEquals("APP_TEST_UPDATE_2", $variable['body']['key']);
|
||||
$this->assertEmpty($variable['body']['value']);
|
||||
$this->assertTrue($variable['body']['secret']);
|
||||
|
||||
// convert secret variable to non-secret
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'APP_TEST_UPDATE',
|
||||
'secret' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
@@ -410,4 +448,53 @@ class FunctionsConsoleClientTest extends Scope
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function testVariableE2E(): void
|
||||
{
|
||||
$function = $this->createFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'runtime' => 'node-18.0',
|
||||
'name' => 'Variable E2E Test',
|
||||
'entrypoint' => 'index.js',
|
||||
'logging' => false,
|
||||
'execute' => ['any']
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
$this->assertFalse($function['body']['logging']);
|
||||
$this->assertNotEmpty($function['body']['$id']);
|
||||
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
// create variable
|
||||
$variable = $this->createVariable($functionId, [
|
||||
'key' => 'CUSTOM_VARIABLE',
|
||||
'value' => 'a_secret_value',
|
||||
'secret' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $variable['headers']['status-code']);
|
||||
$this->assertNotEmpty($variable['body']['$id']);
|
||||
$this->assertEquals('CUSTOM_VARIABLE', $variable['body']['key']);
|
||||
$this->assertEquals('', $variable['body']['value']);
|
||||
$this->assertEquals(true, $variable['body']['secret']);
|
||||
|
||||
$deploymentId = $this->setupDeployment($functionId, [
|
||||
'entrypoint' => 'index.js',
|
||||
'code' => $this->packageFunction('node'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$execution = $this->createExecution($functionId);
|
||||
|
||||
$this->assertEquals(201, $execution['headers']['status-code']);
|
||||
$this->assertEmpty($execution['body']['logs']);
|
||||
$this->assertEmpty($execution['body']['errors']);
|
||||
$body = json_decode($execution['body']['responseBody']);
|
||||
$this->assertEquals('a_secret_value', $body->CUSTOM_VARIABLE);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
'timeout' => 10,
|
||||
]);
|
||||
|
||||
$functionId = $functionId = $function['body']['$id'] ?? '';
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
$dateValidator = new DatetimeValidator();
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
@@ -356,7 +356,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
$this->assertNotEmpty($function['body']['$id']);
|
||||
|
||||
$functionId = $functionId = $function['body']['$id'] ?? '';
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
$deployments = $this->listDeployments($functionId);
|
||||
|
||||
@@ -1898,7 +1898,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertFalse($function['body']['logging']);
|
||||
$this->assertNotEmpty($function['body']['$id']);
|
||||
|
||||
$functionId = $functionId = $function['body']['$id'] ?? '';
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('node'),
|
||||
|
||||
@@ -3923,10 +3923,14 @@ class ProjectsConsoleClientTest extends Scope
|
||||
'x-appwrite-mode' => 'admin',
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'APP_TEST',
|
||||
'value' => 'TESTINGVALUE'
|
||||
'value' => 'TESTINGVALUE',
|
||||
'secret' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $variable['headers']['status-code']);
|
||||
$this->assertEquals('APP_TEST', $variable['body']['key']);
|
||||
$this->assertEquals('TESTINGVALUE', $variable['body']['value']);
|
||||
$this->assertFalse($variable['body']['secret']);
|
||||
$variableId = $variable['body']['$id'];
|
||||
|
||||
// test for secret variable
|
||||
@@ -4047,6 +4051,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals("APP_TEST_1", $response['body']['key']);
|
||||
$this->assertEmpty($response['body']['value']);
|
||||
$this->assertTrue($response['body']['secret']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
@@ -4118,6 +4123,17 @@ class ProjectsConsoleClientTest extends Scope
|
||||
$this->assertEquals("APP_TEST_UPDATE_1", $variable['body']['key']);
|
||||
$this->assertEmpty($variable['body']['value']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/project/variables/' . $data['secretVariableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $data['projectId'],
|
||||
'x-appwrite-mode' => 'admin',
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'APP_TEST_UPDATE_1',
|
||||
'secret' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/project/variables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $data['projectId'],
|
||||
|
||||
@@ -44,7 +44,7 @@ trait SitesBase
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]));
|
||||
$this->assertEquals('ready', $deployment['body']['status'], 'Deployment status is not ready, deployment: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT));
|
||||
}, 50000, 500);
|
||||
}, 100000, 500);
|
||||
|
||||
return $deploymentId;
|
||||
}
|
||||
@@ -101,6 +101,46 @@ trait SitesBase
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function getVariable(string $siteId, string $variableId): mixed
|
||||
{
|
||||
$variable = $this->client->call(Client::METHOD_GET, '/sites/' . $siteId . '/variables/' . $variableId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function listVariables(string $siteId, mixed $params = []): mixed
|
||||
{
|
||||
$variables = $this->client->call(Client::METHOD_GET, '/sites/' . $siteId . '/variables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $params);
|
||||
|
||||
return $variables;
|
||||
}
|
||||
|
||||
protected function updateVariable(string $siteId, string $variableId, mixed $params): mixed
|
||||
{
|
||||
$variable = $this->client->call(Client::METHOD_PUT, '/sites/' . $siteId . '/variables/' . $variableId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $params);
|
||||
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function deleteVariable(string $siteId, string $variableId): mixed
|
||||
{
|
||||
$variable = $this->client->call(Client::METHOD_DELETE, '/sites/' . $siteId . '/variables/' . $variableId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function getSite(string $siteId): mixed
|
||||
{
|
||||
$site = $this->client->call(Client::METHOD_GET, '/sites/' . $siteId, array_merge([
|
||||
|
||||
@@ -65,6 +65,264 @@ class SitesCustomServerTest extends Scope
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testConsoleAvailabilityEndpoint(): void
|
||||
{
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Test Site',
|
||||
'framework' => 'other',
|
||||
'buildRuntime' => 'ssr-22',
|
||||
'outputDirectory' => './',
|
||||
'subdomain' => 'test-site',
|
||||
'fallbackFile' => null,
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
$rule = $this->getSiteDomain($siteId);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/console/resources', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'type' => 'rules',
|
||||
'value' => $rule,
|
||||
]);
|
||||
|
||||
$this->assertEquals(409, $response['headers']['status-code']); // domain unavailable
|
||||
|
||||
$nonExistingDomain = "non-existent-subdomain.sites.localhost";
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/console/resources', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'type' => 'rules',
|
||||
'value' => $nonExistingDomain,
|
||||
]);
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']); // domain available
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
|
||||
$this->assertEventually(function () use ($siteId) {
|
||||
$rule = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::equal('resourceId', [$siteId])
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals(0, $rule['body']['total']);
|
||||
}, 5000, 500);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/console/resources', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'type' => 'rules',
|
||||
'value' => $rule,
|
||||
]);
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']); // domain available as site is deleted
|
||||
}
|
||||
|
||||
public function testVariables(): void
|
||||
{
|
||||
$site = $this->createSite([
|
||||
'buildRuntime' => 'ssr-22',
|
||||
'fallbackFile' => null,
|
||||
'framework' => 'other',
|
||||
'name' => 'Test Site',
|
||||
'outputDirectory' => './',
|
||||
'siteId' => ID::unique()
|
||||
]);
|
||||
|
||||
$siteId = $site['body']['$id'] ?? '';
|
||||
|
||||
$this->assertEquals(201, $site['headers']['status-code']);
|
||||
$this->assertNotEmpty($site['body']['$id']);
|
||||
$this->assertEquals('Test Site', $site['body']['name']);
|
||||
|
||||
$variable = $this->createVariable($siteId, [
|
||||
'key' => 'siteKey1',
|
||||
'value' => 'siteValue1',
|
||||
'secret' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $variable['headers']['status-code']);
|
||||
$this->assertNotEmpty($variable['body']['$id']);
|
||||
$this->assertEquals('siteKey1', $variable['body']['key']);
|
||||
$this->assertEquals('siteValue1', $variable['body']['value']);
|
||||
$this->assertEquals(false, $variable['body']['secret']);
|
||||
|
||||
$variable2 = $this->createVariable($siteId, [
|
||||
'key' => 'siteKey2',
|
||||
'value' => 'siteValue2',
|
||||
'secret' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $variable2['headers']['status-code']);
|
||||
$this->assertNotEmpty($variable2['body']['$id']);
|
||||
$this->assertEquals('siteKey2', $variable2['body']['key']);
|
||||
$this->assertEquals('siteValue2', $variable2['body']['value']);
|
||||
$this->assertEquals(false, $variable2['body']['secret']);
|
||||
|
||||
$secretVariable = $this->createVariable($siteId, [
|
||||
'key' => 'siteKey3',
|
||||
'value' => 'siteValue3',
|
||||
'secret' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $secretVariable['headers']['status-code']);
|
||||
$this->assertNotEmpty($secretVariable['body']['$id']);
|
||||
$this->assertEquals('siteKey3', $secretVariable['body']['key']);
|
||||
$this->assertEquals('', $secretVariable['body']['value']);
|
||||
$this->assertEquals(true, $secretVariable['body']['secret']);
|
||||
|
||||
$variable = $this->getVariable($siteId, $variable['body']['$id']);
|
||||
|
||||
$this->assertEquals(200, $variable['headers']['status-code']);
|
||||
$this->assertNotEmpty($variable['body']['$id']);
|
||||
$this->assertEquals('siteKey1', $variable['body']['key']);
|
||||
$this->assertEquals('siteValue1', $variable['body']['value']);
|
||||
$this->assertEquals(false, $variable['body']['secret']);
|
||||
|
||||
$secretVariable = $this->getVariable($siteId, $secretVariable['body']['$id']);
|
||||
|
||||
$this->assertEquals(200, $secretVariable['headers']['status-code']);
|
||||
$this->assertNotEmpty($secretVariable['body']['$id']);
|
||||
$this->assertEquals('siteKey3', $secretVariable['body']['key']);
|
||||
$this->assertEquals('', $secretVariable['body']['value']);
|
||||
$this->assertEquals(true, $secretVariable['body']['secret']);
|
||||
|
||||
$variable = $this->updateVariable($siteId, $variable['body']['$id'], [
|
||||
'key' => 'siteKey1Updated',
|
||||
'value' => 'siteValue1Updated',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $variable['headers']['status-code']);
|
||||
$this->assertNotEmpty($variable['body']['$id']);
|
||||
$this->assertEquals('siteKey1Updated', $variable['body']['key']);
|
||||
$this->assertEquals('siteValue1Updated', $variable['body']['value']);
|
||||
$this->assertEquals(false, $variable['body']['secret']);
|
||||
|
||||
$variable = $this->updateVariable($siteId, $variable['body']['$id'], [
|
||||
'key' => 'siteKey1Updated',
|
||||
'secret' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $variable['headers']['status-code']);
|
||||
$this->assertNotEmpty($variable['body']['$id']);
|
||||
$this->assertEquals('siteKey1Updated', $variable['body']['key']);
|
||||
$this->assertEquals('', $variable['body']['value']);
|
||||
$this->assertEquals(true, $variable['body']['secret']);
|
||||
|
||||
$secretVariable = $this->updateVariable($siteId, $secretVariable['body']['$id'], [
|
||||
'key' => 'siteKey3',
|
||||
'value' => 'siteValue3Updated',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $secretVariable['headers']['status-code']);
|
||||
$this->assertNotEmpty($secretVariable['body']['$id']);
|
||||
$this->assertEquals('siteKey3', $secretVariable['body']['key']);
|
||||
$this->assertEquals('', $secretVariable['body']['value']);
|
||||
$this->assertEquals(true, $secretVariable['body']['secret']);
|
||||
|
||||
$response = $this->updateVariable($siteId, $secretVariable['body']['$id'], [
|
||||
'key' => 'siteKey3',
|
||||
'secret' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$secretVariable = $this->getVariable($siteId, $secretVariable['body']['$id']);
|
||||
|
||||
$this->assertEquals(200, $secretVariable['headers']['status-code']);
|
||||
$this->assertNotEmpty($secretVariable['body']['$id']);
|
||||
$this->assertEquals('siteKey3', $secretVariable['body']['key']);
|
||||
$this->assertEquals('', $secretVariable['body']['value']);
|
||||
$this->assertEquals(true, $secretVariable['body']['secret']);
|
||||
|
||||
$variables = $this->listVariables($siteId);
|
||||
|
||||
$this->assertEquals(200, $variables['headers']['status-code']);
|
||||
$this->assertCount(3, $variables['body']['variables']);
|
||||
|
||||
$response = $this->deleteVariable($siteId, $variable['body']['$id']);
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
$response = $this->deleteVariable($siteId, $variable2['body']['$id']);
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
$response = $this->deleteVariable($siteId, $secretVariable['body']['$id']);
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
$variables = $this->listVariables($siteId);
|
||||
|
||||
$this->assertEquals(200, $variables['headers']['status-code']);
|
||||
$this->assertCount(0, $variables['body']['variables']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testVariablesE2E(): void
|
||||
{
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Astro site',
|
||||
'framework' => 'astro',
|
||||
'adapter' => 'ssr',
|
||||
'buildRuntime' => 'ssr-22',
|
||||
'outputDirectory' => './dist',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'fallbackFile' => '',
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
$secretVariable = $this->createVariable($siteId, [
|
||||
'key' => 'name',
|
||||
'value' => 'Appwrite',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $secretVariable['headers']['status-code']);
|
||||
$this->assertNotEmpty($secretVariable['body']['$id']);
|
||||
$this->assertEquals('name', $secretVariable['body']['key']);
|
||||
$this->assertEquals('', $secretVariable['body']['value']);
|
||||
$this->assertEquals(true, $secretVariable['body']['secret']);
|
||||
|
||||
$deploymentId = $this->setupDeployment($siteId, [
|
||||
'code' => $this->packageSite('astro'),
|
||||
'activate' => 'true'
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$domain = $this->getSiteDomain($siteId);
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Env variable is Appwrite", $response['body']);
|
||||
$this->assertStringNotContainsString("Variable not found", $response['body']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testListSites(): void
|
||||
{
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { defineConfig } from 'astro/config';
|
||||
import node from '@astrojs/node';
|
||||
import 'dotenv/config';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'server',
|
||||
adapter: node({
|
||||
mode: 'standalone'
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "my-astro-app",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^9.0.2",
|
||||
"astro": "^5.2.5",
|
||||
"dotenv": "^16.4.7"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
const value = import.meta.env.name || 'Variable not found';
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Astro SSR</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Env variable is {value}</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"include": [".astro/types.d.ts", "**/*"],
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user