mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
restore
This commit is contained in:
@@ -2974,6 +2974,729 @@ $collections = [
|
||||
],
|
||||
]
|
||||
],
|
||||
'videos' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'videos',
|
||||
'$name' => 'Videos',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'bucketId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'fileId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'size',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'duration',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'width',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'height',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'videoCodec',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'videoBitrate',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'videoFramerate',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'audioCodec',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'audioBitrate',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'audioSamplerate',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_bucket_file',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['bucketId', 'fileId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
]
|
||||
],
|
||||
'videos_renditions' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'videos_renditions',
|
||||
'$name' => 'Videos_renditions',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'videoId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'profileId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'name',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'startedAt',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'endedAt',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'path',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'targetDuration',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'duration',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'width',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'height',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'videoCodec',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'videoBitrate',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'videoFramerate',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'audioCodec',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'audioBitrate',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'audioSamplerate',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'metadata',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 4096,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
],
|
||||
[
|
||||
'$id' => 'status',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'progress',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'stream',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_video_stream_profile',
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
'attributes' => ['videoId', 'stream', 'profileId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
]
|
||||
],
|
||||
'videos_renditions_segments' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'videos_rendition_segments',
|
||||
'$name' => 'Videos_rendition_segments',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'renditionId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'fileName',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'path',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'duration',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'representationId',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'isInit',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_renditionId',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['renditionId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
]
|
||||
],
|
||||
'videos_profiles' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'videos_profiles',
|
||||
'$name' => 'Videos_profiles',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'name',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'videoBitrate',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'audioBitrate',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'width',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'height',
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'stream',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_unique',
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
'attributes' => ['stream', 'width', 'height', 'videoBitrate', 'audioBitrate' ],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
]
|
||||
],
|
||||
'videos_subtitles' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'videos_subtitles',
|
||||
'$name' => 'Videos_subtitles',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'videoId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'bucketId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'fileId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'path',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'name',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'code',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'default',
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => false,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'targetDuration',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'status',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
|
||||
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_bucket_video_subtitle',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['videoId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
]
|
||||
],
|
||||
'videos_subtitles_segments' => [
|
||||
'$collection' => Database::METADATA,
|
||||
'$id' => 'videos_subtitles_segments',
|
||||
'$name' => 'Videos_subtitles_segments',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => 'subtitleId',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'fileName',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'path',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'duration',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_subtitleId',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['subtitleId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
return $collections;
|
||||
@@ -78,14 +78,17 @@ App::post('/v1/projects')
|
||||
}
|
||||
|
||||
$projectId = ($projectId == 'unique()') ? $dbForConsole->getId() : $projectId;
|
||||
|
||||
if ($projectId === 'console') {
|
||||
throw new Exception("'console' is a reserved project.", 400, Exception::PROJECT_RESERVED_PROJECT);
|
||||
}
|
||||
|
||||
$project = $dbForConsole->createDocument('projects', new Document([
|
||||
'$id' => $projectId == 'unique()' ? $dbForConsole->getId() : $projectId,
|
||||
'$id' => $projectId,
|
||||
'$read' => ['team:' . $teamId],
|
||||
'$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'],
|
||||
'name' => $name,
|
||||
'teamInternalId' => $team->getInternalId(),
|
||||
'teamId' => $team->getId(),
|
||||
'description' => $description,
|
||||
'logo' => $logo,
|
||||
@@ -109,8 +112,8 @@ App::post('/v1/projects')
|
||||
/** @var array $collections */
|
||||
$collections = Config::getParam('collections', []);
|
||||
|
||||
$dbForProject->setNamespace("_{$project->getId()}");
|
||||
$dbForProject->create('appwrite');
|
||||
$dbForProject->setNamespace("_{$project->getInternalId()}");
|
||||
$dbForProject->create(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
$audit->setup();
|
||||
@@ -185,7 +188,7 @@ App::get('/v1/projects')
|
||||
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursor', '', new UID(), 'ID of the project used as the starting point for the query, excluding the project itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
@@ -283,15 +286,15 @@ App::get('/v1/projects/:projectId/usage')
|
||||
],
|
||||
];
|
||||
|
||||
$dbForProject->setNamespace("_{$projectId}");
|
||||
$dbForProject->setNamespace("_{$project->getInternalId()}");
|
||||
|
||||
$metrics = [
|
||||
'requests',
|
||||
'network',
|
||||
'executions',
|
||||
'users.count',
|
||||
'database.documents.count',
|
||||
'database.collections.count',
|
||||
'databases.documents.count',
|
||||
'databases.collections.count',
|
||||
'storage.total'
|
||||
];
|
||||
|
||||
@@ -338,8 +341,8 @@ App::get('/v1/projects/:projectId/usage')
|
||||
'requests' => $stats['requests'],
|
||||
'network' => $stats['network'],
|
||||
'functions' => $stats['executions'],
|
||||
'documents' => $stats['database.documents.count'],
|
||||
'collections' => $stats['database.collections.count'],
|
||||
'documents' => $stats['databases.documents.count'],
|
||||
'collections' => $stats['databases.collections.count'],
|
||||
'users' => $stats['users.count'],
|
||||
'storage' => $stats['storage.total']
|
||||
]);
|
||||
@@ -380,17 +383,17 @@ App::patch('/v1/projects/:projectId')
|
||||
}
|
||||
|
||||
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('description', $description)
|
||||
->setAttribute('logo', $logo)
|
||||
->setAttribute('url', $url)
|
||||
->setAttribute('legalName', $legalName)
|
||||
->setAttribute('legalCountry', $legalCountry)
|
||||
->setAttribute('legalState', $legalState)
|
||||
->setAttribute('legalCity', $legalCity)
|
||||
->setAttribute('legalAddress', $legalAddress)
|
||||
->setAttribute('legalTaxId', $legalTaxId)
|
||||
->setAttribute('search', implode(' ', [$projectId, $name])));
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('description', $description)
|
||||
->setAttribute('logo', $logo)
|
||||
->setAttribute('url', $url)
|
||||
->setAttribute('legalName', $legalName)
|
||||
->setAttribute('legalCountry', $legalCountry)
|
||||
->setAttribute('legalState', $legalState)
|
||||
->setAttribute('legalCity', $legalCity)
|
||||
->setAttribute('legalAddress', $legalAddress)
|
||||
->setAttribute('legalTaxId', $legalTaxId)
|
||||
->setAttribute('search', implode(' ', [$projectId, $name])));
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
@@ -598,12 +601,11 @@ App::post('/v1/projects/:projectId/webhooks')
|
||||
|
||||
$security = (bool) filter_var($security, FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
|
||||
|
||||
$webhook = new Document([
|
||||
'$id' => $dbForConsole->getId(),
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectId' => $project->getId(),
|
||||
'name' => $name,
|
||||
'events' => $events,
|
||||
@@ -644,7 +646,7 @@ App::get('/v1/projects/:projectId/webhooks')
|
||||
}
|
||||
|
||||
$webhooks = $dbForConsole->find('webhooks', [
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
], 5000);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
@@ -677,7 +679,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
|
||||
$webhook = $dbForConsole->findOne('webhooks', [
|
||||
new Query('_uid', Query::TYPE_EQUAL, [$webhookId]),
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($webhook === false || $webhook->isEmpty()) {
|
||||
@@ -705,10 +707,9 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
->param('security', false, new Boolean(true), 'Certificate verification, false for disabled or true for enabled.')
|
||||
->param('httpUser', '', new Text(256), 'Webhook HTTP user. Max length: 256 chars.', true)
|
||||
->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true)
|
||||
->param('signatureKey', null, new Text(256), 'Webhook signature key. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $projectId, string $webhookId, string $name, array $events, string $url, bool $security, string $httpUser, string $httpPass, string $signatureKey, Response $response, Database $dbForConsole) {
|
||||
->action(function (string $projectId, string $webhookId, string $name, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) {
|
||||
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
@@ -720,7 +721,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
|
||||
$webhook = $dbForConsole->findOne('webhooks', [
|
||||
new Query('_uid', Query::TYPE_EQUAL, [$webhookId]),
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($webhook === false || $webhook->isEmpty()) {
|
||||
@@ -736,10 +737,45 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
->setAttribute('httpPass', $httpPass)
|
||||
;
|
||||
|
||||
if (!empty($signatureKey)) {
|
||||
$webhook->setAttribute('signatureKey', $signatureKey);
|
||||
$dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook);
|
||||
$dbForConsole->deleteCachedDocument('projects', $project->getId());
|
||||
|
||||
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
|
||||
->desc('Update Webhook Signature Key')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'projects')
|
||||
->label('sdk.method', 'updateWebhookSignature')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_WEBHOOK)
|
||||
->param('projectId', null, new UID(), 'Project unique ID.')
|
||||
->param('webhookId', null, new UID(), 'Webhook unique ID.')
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) {
|
||||
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$webhook = $dbForConsole->findOne('webhooks', [
|
||||
new Query('_uid', Query::TYPE_EQUAL, [$webhookId]),
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($webhook === false || $webhook->isEmpty()) {
|
||||
throw new Exception('Webhook not found', 404, Exception::WEBHOOK_NOT_FOUND);
|
||||
}
|
||||
|
||||
$webhook->setAttribute('signatureKey', \bin2hex(\random_bytes(64)));
|
||||
|
||||
$dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook);
|
||||
$dbForConsole->deleteCachedDocument('projects', $project->getId());
|
||||
|
||||
@@ -769,7 +805,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
|
||||
$webhook = $dbForConsole->findOne('webhooks', [
|
||||
new Query('_uid', Query::TYPE_EQUAL, [$webhookId]),
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($webhook === false || $webhook->isEmpty()) {
|
||||
@@ -798,9 +834,10 @@ App::post('/v1/projects/:projectId/keys')
|
||||
->param('projectId', null, new UID(), 'Project unique ID.')
|
||||
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
|
||||
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
|
||||
->param('expire', 0, new Integer(), 'Key expiration time in Unix timestamp. Use 0 for unlimited expiration.', true)
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $projectId, string $name, array $scopes, Response $response, Database $dbForConsole) {
|
||||
->action(function (string $projectId, string $name, array $scopes, int $expire, Response $response, Database $dbForConsole) {
|
||||
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
@@ -812,9 +849,11 @@ App::post('/v1/projects/:projectId/keys')
|
||||
'$id' => $dbForConsole->getId(),
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectId' => $project->getId(),
|
||||
'name' => $name,
|
||||
'scopes' => $scopes,
|
||||
'expire' => $expire,
|
||||
'secret' => \bin2hex(\random_bytes(128)),
|
||||
]);
|
||||
|
||||
@@ -848,7 +887,7 @@ App::get('/v1/projects/:projectId/keys')
|
||||
}
|
||||
|
||||
$keys = $dbForConsole->find('keys', [
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]),
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()]),
|
||||
], 5000);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
@@ -881,7 +920,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
|
||||
|
||||
$key = $dbForConsole->findOne('keys', [
|
||||
new Query('_uid', Query::TYPE_EQUAL, [$keyId]),
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($key === false || $key->isEmpty()) {
|
||||
@@ -905,9 +944,10 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||
->param('keyId', null, new UID(), 'Key unique ID.')
|
||||
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
|
||||
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
|
||||
->param('expire', 0, new Integer(), 'Key expiration time in Unix timestamp. Use 0 for unlimited expiration.', true)
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $projectId, string $keyId, string $name, array $scopes, Response $response, Database $dbForConsole) {
|
||||
->action(function (string $projectId, string $keyId, string $name, array $scopes, int $expire, Response $response, Database $dbForConsole) {
|
||||
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
@@ -917,7 +957,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||
|
||||
$key = $dbForConsole->findOne('keys', [
|
||||
new Query('_uid', Query::TYPE_EQUAL, [$keyId]),
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($key === false || $key->isEmpty()) {
|
||||
@@ -927,6 +967,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||
$key
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('scopes', $scopes)
|
||||
->setAttribute('expire', $expire)
|
||||
;
|
||||
|
||||
$dbForConsole->updateDocument('keys', $key->getId(), $key);
|
||||
@@ -959,7 +1000,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
|
||||
|
||||
$key = $dbForConsole->findOne('keys', [
|
||||
new Query('_uid', Query::TYPE_EQUAL, [$keyId]),
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($key === false || $key->isEmpty()) {
|
||||
@@ -1004,14 +1045,13 @@ App::post('/v1/projects/:projectId/platforms')
|
||||
'$id' => $dbForConsole->getId(),
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectId' => $project->getId(),
|
||||
'type' => $type,
|
||||
'name' => $name,
|
||||
'key' => $key,
|
||||
'store' => $store,
|
||||
'hostname' => $hostname,
|
||||
'dateCreated' => \time(),
|
||||
'dateUpdated' => \time(),
|
||||
'hostname' => $hostname
|
||||
]);
|
||||
|
||||
$platform = $dbForConsole->createDocument('platforms', $platform);
|
||||
@@ -1077,7 +1117,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
|
||||
|
||||
$platform = $dbForConsole->findOne('platforms', [
|
||||
new Query('_uid', Query::TYPE_EQUAL, [$platformId]),
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($platform === false || $platform->isEmpty()) {
|
||||
@@ -1114,7 +1154,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
|
||||
|
||||
$platform = $dbForConsole->findOne('platforms', [
|
||||
new Query('_uid', Query::TYPE_EQUAL, [$platformId]),
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($platform === false || $platform->isEmpty()) {
|
||||
@@ -1123,7 +1163,6 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
|
||||
|
||||
$platform
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('dateUpdated', \time())
|
||||
->setAttribute('key', $key)
|
||||
->setAttribute('store', $store)
|
||||
->setAttribute('hostname', $hostname)
|
||||
@@ -1159,7 +1198,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
|
||||
|
||||
$platform = $dbForConsole->findOne('platforms', [
|
||||
new Query('_uid', Query::TYPE_EQUAL, [$platformId]),
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($platform === false || $platform->isEmpty()) {
|
||||
@@ -1199,7 +1238,7 @@ App::post('/v1/projects/:projectId/domains')
|
||||
|
||||
$document = $dbForConsole->findOne('domains', [
|
||||
new Query('domain', Query::TYPE_EQUAL, [$domain]),
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]),
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()]),
|
||||
]);
|
||||
|
||||
if ($document && !$document->isEmpty()) {
|
||||
@@ -1218,6 +1257,7 @@ App::post('/v1/projects/:projectId/domains')
|
||||
'$id' => $dbForConsole->getId(),
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['role:all'],
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectId' => $project->getId(),
|
||||
'updated' => \time(),
|
||||
'domain' => $domain->get(),
|
||||
@@ -1257,7 +1297,7 @@ App::get('/v1/projects/:projectId/domains')
|
||||
}
|
||||
|
||||
$domains = $dbForConsole->find('domains', [
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
], 5000);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
@@ -1290,7 +1330,7 @@ App::get('/v1/projects/:projectId/domains/:domainId')
|
||||
|
||||
$domain = $dbForConsole->findOne('domains', [
|
||||
new Query('_uid', Query::TYPE_EQUAL, [$domainId]),
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($domain === false || $domain->isEmpty()) {
|
||||
@@ -1324,7 +1364,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
|
||||
|
||||
$domain = $dbForConsole->findOne('domains', [
|
||||
new Query('_uid', Query::TYPE_EQUAL, [$domainId]),
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($domain === false || $domain->isEmpty()) {
|
||||
@@ -1384,7 +1424,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
|
||||
|
||||
$domain = $dbForConsole->findOne('domains', [
|
||||
new Query('_uid', Query::TYPE_EQUAL, [$domainId]),
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($domain === false || $domain->isEmpty()) {
|
||||
@@ -1401,3 +1441,5 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ App::post('/v1/storage/buckets')
|
||||
->param('bucketId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', '', new Text(128), 'Bucket name')
|
||||
->param('permission', null, new WhiteList(['file', 'bucket']), 'Permissions type model to use for reading files in this bucket. You can use bucket-level permission set once on the bucket using the `read` and `write` params, or you can set file-level permission where each file read and write params will decide who has access to read and write to each file individually. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true)
|
||||
->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self-hosted setups you can change the max limit by changing the `_APP_STORAGE_LIMIT` environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true)
|
||||
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
|
||||
@@ -107,8 +107,6 @@ App::post('/v1/storage/buckets')
|
||||
$bucket = $dbForProject->createDocument('buckets', new Document([
|
||||
'$id' => $bucketId,
|
||||
'$collection' => 'buckets',
|
||||
'dateCreated' => \time(),
|
||||
'dateUpdated' => \time(),
|
||||
'name' => $name,
|
||||
'permission' => $permission,
|
||||
'maximumFileSize' => $maximumFileSize,
|
||||
@@ -158,7 +156,7 @@ App::get('/v1/storage/buckets')
|
||||
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
|
||||
->param('cursor', '', new UID(), 'ID of the bucket used as the starting point for the query, excluding the bucket itself. Should be used for efficient pagination when working with large sets of data.', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
@@ -226,8 +224,8 @@ App::put('/v1/storage/buckets/:bucketId')
|
||||
->param('bucketId', '', new UID(), 'Bucket unique ID.')
|
||||
->param('name', null, new Text(128), 'Bucket name', false)
|
||||
->param('permission', null, new WhiteList(['file', 'bucket']), 'Permissions type model to use for reading files in this bucket. You can use bucket-level permission set once on the bucket using the `read` and `write` params, or you can set file-level permission where each file read and write params will decide who has access to read and write to each file individually. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
|
||||
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('enabled', true, new Boolean(true), 'Is bucket enabled?', true)
|
||||
->param('maximumFileSize', null, new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '. For self hosted version you can change the limit by changing _APP_STORAGE_LIMIT environment variable. [Learn more about storage environment variables](docs/environment-variables#storage)', true)
|
||||
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
|
||||
@@ -254,15 +252,15 @@ App::put('/v1/storage/buckets/:bucketId')
|
||||
$antivirus ??= $bucket->getAttribute('antivirus', true);
|
||||
|
||||
$bucket = $dbForProject->updateDocument('buckets', $bucket->getId(), $bucket
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('$read', $read)
|
||||
->setAttribute('$write', $write)
|
||||
->setAttribute('maximumFileSize', $maximumFileSize)
|
||||
->setAttribute('allowedFileExtensions', $allowedFileExtensions)
|
||||
->setAttribute('enabled', (bool) filter_var($enabled, FILTER_VALIDATE_BOOLEAN))
|
||||
->setAttribute('encryption', (bool) filter_var($encryption, FILTER_VALIDATE_BOOLEAN))
|
||||
->setAttribute('permission', $permission)
|
||||
->setAttribute('antivirus', (bool) filter_var($antivirus, FILTER_VALIDATE_BOOLEAN)));
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('$read', $read)
|
||||
->setAttribute('$write', $write)
|
||||
->setAttribute('maximumFileSize', $maximumFileSize)
|
||||
->setAttribute('allowedFileExtensions', $allowedFileExtensions)
|
||||
->setAttribute('enabled', (bool) filter_var($enabled, FILTER_VALIDATE_BOOLEAN))
|
||||
->setAttribute('encryption', (bool) filter_var($encryption, FILTER_VALIDATE_BOOLEAN))
|
||||
->setAttribute('permission', $permission)
|
||||
->setAttribute('antivirus', (bool) filter_var($antivirus, FILTER_VALIDATE_BOOLEAN)));
|
||||
|
||||
$audits
|
||||
->setResource('storage/buckets/' . $bucket->getId())
|
||||
@@ -344,8 +342,8 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new CustomId(), 'File ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('file', [], new File(), 'Binary file.', false)
|
||||
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
@@ -442,8 +440,8 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
}
|
||||
|
||||
/**
|
||||
* Validators
|
||||
*/
|
||||
* Validators
|
||||
*/
|
||||
// Check if file type is allowed
|
||||
$allowedFileExtensions = $bucket->getAttribute('allowedFileExtensions', []);
|
||||
$fileExt = new FileExt($allowedFileExtensions);
|
||||
@@ -546,7 +544,6 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
'$id' => $fileId,
|
||||
'$read' => $read,
|
||||
'$write' => $write,
|
||||
'dateCreated' => \time(),
|
||||
'bucketId' => $bucket->getId(),
|
||||
'name' => $fileName,
|
||||
'path' => $path,
|
||||
@@ -613,7 +610,6 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
'$id' => $fileId,
|
||||
'$read' => $read,
|
||||
'$write' => $write,
|
||||
'dateCreated' => \time(),
|
||||
'bucketId' => $bucket->getId(),
|
||||
'name' => $fileName,
|
||||
'path' => $path,
|
||||
@@ -654,7 +650,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
$events
|
||||
->setParam('bucketId', $bucket->getId())
|
||||
->setParam('fileId', $file->getId())
|
||||
->setContext($bucket)
|
||||
->setContext('bucket', $bucket)
|
||||
;
|
||||
|
||||
$metadata = null; // was causing leaks as it was passed by reference
|
||||
@@ -680,7 +676,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of files to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursor', '', new UID(), 'ID of the file used as the starting point for the query, excluding the file itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
@@ -923,7 +919,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
->addHeader('Expires', $date)
|
||||
->addHeader('X-Appwrite-Cache', 'hit')
|
||||
->send($data)
|
||||
;
|
||||
;
|
||||
}
|
||||
|
||||
$source = $deviceFiles->read($path);
|
||||
@@ -1292,8 +1288,8 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->label('sdk.response.model', Response::MODEL_FILE)
|
||||
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new UID(), 'File unique ID.')
|
||||
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('user')
|
||||
@@ -1361,7 +1357,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
$events
|
||||
->setParam('bucketId', $bucket->getId())
|
||||
->setParam('fileId', $file->getId())
|
||||
->setContext($bucket)
|
||||
->setContext('bucket', $bucket)
|
||||
;
|
||||
|
||||
$audits->setResource('file/' . $file->getId());
|
||||
@@ -1463,7 +1459,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
$events
|
||||
->setParam('bucketId', $bucket->getId())
|
||||
->setParam('fileId', $file->getId())
|
||||
->setContext($bucket)
|
||||
->setContext('bucket', $bucket)
|
||||
->setPayload($response->output($file, Response::MODEL_FILE))
|
||||
;
|
||||
|
||||
@@ -1680,4 +1676,4 @@ App::get('/v1/storage/:bucketId/usage')
|
||||
}
|
||||
|
||||
$response->dynamic($usage, Response::MODEL_USAGE_BUCKETS);
|
||||
});
|
||||
});
|
||||
@@ -63,7 +63,6 @@ App::post('/v1/teams')
|
||||
'$write' => ['team:' . $teamId . '/owner'],
|
||||
'name' => $name,
|
||||
'total' => ($isPrivilegedUser || $isAppUser) ? 0 : 1,
|
||||
'dateCreated' => \time(),
|
||||
'search' => implode(' ', [$teamId, $name]),
|
||||
])));
|
||||
|
||||
@@ -74,7 +73,9 @@ App::post('/v1/teams')
|
||||
'$read' => ['user:' . $user->getId(), 'team:' . $team->getId()],
|
||||
'$write' => ['user:' . $user->getId(), 'team:' . $team->getId() . '/owner'],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'teamId' => $team->getId(),
|
||||
'teamInternalId' => $team->getInternalId(),
|
||||
'roles' => $roles,
|
||||
'invited' => \time(),
|
||||
'joined' => \time(),
|
||||
@@ -118,7 +119,7 @@ App::get('/v1/teams')
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of teams to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursor', '', new UID(), 'ID of the team used as the starting point for the query, excluding the team itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
@@ -367,7 +368,9 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:' . $invitee->getId(), 'team:' . $team->getId() . '/owner'],
|
||||
'userId' => $invitee->getId(),
|
||||
'userInternalId' => $invitee->getInternalId(),
|
||||
'teamId' => $team->getId(),
|
||||
'teamInternalId' => $team->getInternalId(),
|
||||
'roles' => $roles,
|
||||
'invited' => \time(),
|
||||
'joined' => ($isPrivilegedUser || $isAppUser) ? \time() : 0,
|
||||
@@ -423,9 +426,9 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic(
|
||||
$membership
|
||||
->setAttribute('teamName', $team->getAttribute('name'))
|
||||
->setAttribute('userName', $user->getAttribute('name'))
|
||||
->setAttribute('userEmail', $user->getAttribute('email')),
|
||||
->setAttribute('teamName', $team->getAttribute('name'))
|
||||
->setAttribute('userName', $user->getAttribute('name'))
|
||||
->setAttribute('userEmail', $user->getAttribute('email')),
|
||||
Response::MODEL_MEMBERSHIP
|
||||
);
|
||||
});
|
||||
@@ -446,7 +449,7 @@ App::get('/v1/teams/:teamId/memberships')
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of memberships to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursor', '', new UID(), 'ID of the membership used as the starting point for the query, excluding the membership itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
@@ -683,7 +686,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
}
|
||||
|
||||
$membership // Attach user to team
|
||||
->setAttribute('joined', \time())
|
||||
->setAttribute('joined', \time())
|
||||
->setAttribute('confirm', true)
|
||||
;
|
||||
|
||||
@@ -702,6 +705,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
$session = new Document(array_merge([
|
||||
'$id' => $dbForProject->getId(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => $user->getAttribute('email'),
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
@@ -745,9 +749,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
|
||||
$response->dynamic(
|
||||
$membership
|
||||
->setAttribute('teamName', $team->getAttribute('name'))
|
||||
->setAttribute('userName', $user->getAttribute('name'))
|
||||
->setAttribute('userEmail', $user->getAttribute('email')),
|
||||
->setAttribute('teamName', $team->getAttribute('name'))
|
||||
->setAttribute('userName', $user->getAttribute('name'))
|
||||
->setAttribute('userEmail', $user->getAttribute('email')),
|
||||
Response::MODEL_MEMBERSHIP
|
||||
);
|
||||
});
|
||||
@@ -902,4 +906,4 @@ App::get('/v1/teams/:teamId/logs')
|
||||
'total' => $audit->countLogsByResource($resource),
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Validator\Password;
|
||||
use Appwrite\Auth\Validator\Phone;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
@@ -103,7 +104,7 @@ App::get('/v1/users')
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of users to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursor', '', new UID(), 'ID of the user used as the starting point for the query, excluding the user itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
|
||||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
@@ -406,8 +407,8 @@ App::patch('/v1/users/:userId/verification')
|
||||
->label('scope', 'users.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updateVerification')
|
||||
->label('sdk.description', '/docs/references/users/update-user-verification.md')
|
||||
->label('sdk.method', 'updateEmailVerification')
|
||||
->label('sdk.description', '/docs/references/users/update-user-email-verification.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
@@ -438,6 +439,45 @@ App::patch('/v1/users/:userId/verification')
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/verification/phone')
|
||||
->desc('Update Phone Verification')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.verification')
|
||||
->label('scope', 'users.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updatePhoneVerification')
|
||||
->label('sdk.description', '/docs/references/users/update-user-phone-verification.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('phoneVerification', false, new Boolean(), 'User phone verification status.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function (string $userId, bool $phoneVerification, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('phoneVerification', $phoneVerification));
|
||||
|
||||
$usage
|
||||
->setParam('users.update', 1)
|
||||
;
|
||||
|
||||
$events
|
||||
->setParam('userId', $user->getId())
|
||||
;
|
||||
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/name')
|
||||
->desc('Update Name')
|
||||
->groups(['api', 'users'])
|
||||
@@ -551,15 +591,11 @@ App::patch('/v1/users/:userId/email')
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$isAnonymousUser = is_null($user->getAttribute('email')) && is_null($user->getAttribute('password')); // Check if request is from an anonymous account for converting
|
||||
if (!$isAnonymousUser) {
|
||||
//TODO: Remove previous unique ID.
|
||||
}
|
||||
|
||||
$email = \strtolower($email);
|
||||
|
||||
$user
|
||||
->setAttribute('email', $email)
|
||||
->setAttribute('emailVerification', false)
|
||||
->setAttribute('search', \implode(' ', [$user->getId(), $email, $user->getAttribute('name')]))
|
||||
;
|
||||
|
||||
@@ -570,6 +606,55 @@ App::patch('/v1/users/:userId/email')
|
||||
}
|
||||
|
||||
|
||||
$audits
|
||||
->setResource('user/' . $user->getId())
|
||||
;
|
||||
|
||||
$events
|
||||
->setParam('userId', $user->getId())
|
||||
;
|
||||
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/phone')
|
||||
->desc('Update Phone')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.phone')
|
||||
->label('scope', 'users.write')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'users')
|
||||
->label('sdk.method', 'updatePhone')
|
||||
->label('sdk.description', '/docs/references/users/update-user-phone.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('number', '', new Phone(), 'User phone number.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('audits')
|
||||
->inject('events')
|
||||
->action(function (string $userId, string $number, Response $response, Database $dbForProject, EventAudit $audits, Event $events) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user
|
||||
->setAttribute('phone', $number)
|
||||
->setAttribute('phoneVerification', false)
|
||||
;
|
||||
|
||||
try {
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Email already exists', 409, Exception::USER_EMAIL_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
|
||||
$audits
|
||||
->setResource('user/' . $user->getId())
|
||||
;
|
||||
@@ -862,4 +947,4 @@ App::get('/v1/users/usage')
|
||||
}
|
||||
|
||||
$response->dynamic($usage, Response::MODEL_USAGE_USERS);
|
||||
});
|
||||
});
|
||||
@@ -767,21 +767,38 @@ App::get('/v1/videos/:videoId/streams/:streamId')
|
||||
$response->setContentType('application/x-mpegurl')->send($template->render(false));
|
||||
} else {
|
||||
$adaptations = [];
|
||||
|
||||
foreach ($renditions as $rendition) {
|
||||
$metadata = $rendition->getAttribute('metadata');
|
||||
foreach ($metadata['mpeg-dash']['Period']['AdaptationSet'] as $set) {
|
||||
$adaption = $set['@attributes'];
|
||||
$adaption['baseUrl'] = $baseUrl . '/renditions/' . $rendition->getId();
|
||||
$adaption['representation'] = $set['Representation']['@attributes'];
|
||||
$adaption['representation']['SegmentTemplate'] = $set['Representation']['SegmentTemplate']['@attributes'];
|
||||
$adaption['representation']['segmentTemplate']['segmentTimeline'] = $set['Representation']['SegmentTemplate']['SegmentTimeline'];
|
||||
$adaptations[] = $adaption;
|
||||
}
|
||||
}
|
||||
if (!empty($metadata['Period']['AdaptationSet'])) {
|
||||
foreach ($metadata['Period']['AdaptationSet'] as $set) {
|
||||
$adaption = $set['@attributes'];
|
||||
$adaption['representation'] = $set['Representation']['@attributes'];
|
||||
|
||||
$template = new View(__DIR__ . '/../../views/videos/dash.phtml');
|
||||
$template->setParam('params', $adaptations);
|
||||
$response->setContentType('application/dash+xml')->send($template->render(false));
|
||||
$adaption['representation']['baseUrl'] = $baseUrl . '/renditions/' . $rendition->getId();
|
||||
$adaption['representation']['SegmentList'] = $set['Representation']['SegmentList']['@attributes'];
|
||||
$representationId = 0;
|
||||
$segments = Authorization::skip(fn() => $dbForProject->find('videos_renditions_segments', [
|
||||
new Query('renditionId', Query::TYPE_EQUAL, [$rendition->getId()]),
|
||||
new Query('representationId', Query::TYPE_EQUAL, [$representationId]),
|
||||
], 1000, 0, ['representationId'], ['ASC']));
|
||||
|
||||
foreach ($segments as $segment) {
|
||||
if ($segment->getAttribute('isInit') === 1) {
|
||||
$adaption['representation']['SegmentList']['Initialization'] = $baseUrl . '/renditions/' . $segment->getId() . '/';
|
||||
continue;
|
||||
}
|
||||
$adaption['representation']['SegmentList']['media'][] = $segment->getId();
|
||||
}
|
||||
$adaptations[] = $adaption;
|
||||
$representationId++;
|
||||
}
|
||||
}
|
||||
|
||||
$template = new View(__DIR__ . '/../../views/videos/dash.phtml');
|
||||
$template->setParam('params', $adaptations);
|
||||
$response->setContentType('application/dash+xml')->send($template->render(false));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ const APP_STORAGE_UPLOADS = '/storage/uploads';
|
||||
const APP_STORAGE_FUNCTIONS = '/storage/functions';
|
||||
const APP_STORAGE_BUILDS = '/storage/builds';
|
||||
const APP_STORAGE_CACHE = '/storage/cache';
|
||||
const APP_STORAGE_VIDEOS = '/storage/videos';
|
||||
const APP_STORAGE_CERTIFICATES = '/storage/certificates';
|
||||
const APP_STORAGE_CONFIG = '/storage/config';
|
||||
const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT`
|
||||
@@ -189,6 +190,7 @@ Config::load('storage-logos', __DIR__ . '/config/storage/logos.php');
|
||||
Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php');
|
||||
Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php');
|
||||
Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php');
|
||||
Config::load('videos-profiles', __DIR__ . '/config/videos-profiles.php');
|
||||
|
||||
$user = App::getEnv('_APP_REDIS_USER', '');
|
||||
$pass = App::getEnv('_APP_REDIS_PASS', '');
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
<?php
|
||||
$params = $this->getParam('params', []);
|
||||
|
||||
?>
|
||||
<?php echo '<?xml version="1.0" encoding="utf-8"?>'; ?>
|
||||
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
|
||||
@@ -11,44 +7,15 @@ $params = $this->getParam('params', []);
|
||||
<Period id="0" start="PT0.0S">
|
||||
<?php foreach ($this->getParam('params', []) as $param): ?>
|
||||
<AdaptationSet
|
||||
<?php if (isset($param['id'])):?> id="<?php echo $param['id']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['contentType'])): ?> contentType="<?php echo $param['contentType']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['startWithSAP'])): ?> startWithSAP="<?php echo $param['startWithSAP']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['segmentAlignment'])): ?> segmentAlignment="<?php echo $param['segmentAlignment']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['bitstreamSwitching'])): ?> bitstreamSwitching="<?php echo $param['bitstreamSwitching']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['frameRate'])): ?> frameRate="<?php echo $param['frameRate']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['maxWidth'])): ?> maxWidth="<?php echo $param['maxWidth']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['par'])): ?> par="<?php echo $param['par']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['lang'])): ?> lang="<?php echo $param['lang']; ?>" <?php endif;?>
|
||||
>
|
||||
<BaseURL><?php if (isset($param['baseUrl'])): echo $param['baseUrl']; endif;?></BaseURL>
|
||||
<Representation
|
||||
<?php if (isset($param['representation']['id'])):?> id="<?php echo $param['representation']['id']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['representation']['mimeType'])):?> mimeType="<?php echo $param['representation']['mimeType']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['representation']['codecs'])):?> codecs="<?php echo $param['representation']['codecs']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['representation']['width'])):?> width="<?php echo $param['representation']['width']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['representation']['height'])):?> height="<?php echo $param['representation']['height']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['representation']['sar'])):?> sar="<?php echo $param['representation']['sar']; ?>" <?php endif;?>
|
||||
<?php if (isset($param['representation']['audioSamplingRate'])):?> audioSamplingRate="<?php echo $param['representation']['audioSamplingRate']; ?>" <?php endif;?>
|
||||
>
|
||||
<?php if (isset($param['id'])):?> id="<?php echo $param['id']; ?>"<?php endif;?><?php if (isset($param['contentType'])): ?> contentType="<?php echo $param['contentType']; ?>"<?php endif;?><?php if (isset($param['startWithSAP'])): ?> startWithSAP="<?php echo $param['startWithSAP']; ?>"<?php endif;?><?php if (isset($param['segmentAlignment'])): ?> segmentAlignment="<?php echo $param['segmentAlignment']; ?>"<?php endif;?><?php if (isset($param['bitstreamSwitching'])): ?> bitstreamSwitching="<?php echo $param['bitstreamSwitching']; ?>"<?php endif;?><?php if (isset($param['frameRate'])): ?> frameRate="<?php echo $param['frameRate']; ?>"<?php endif;?><?php if (isset($param['maxWidth'])): ?> maxWidth="<?php echo $param['maxWidth']; ?>"<?php endif;?><?php if (isset($param['par'])): ?> par="<?php echo $param['par']; ?>"<?php endif;?><?php if (isset($param['lang'])): ?> lang="<?php echo $param['lang']; ?>"<?php endif;?>>
|
||||
<Representation<?php if (isset($param['representation']['id'])):?> id="<?php echo $param['representation']['id']; ?>"<?php endif;?><?php if (isset($param['representation']['mimeType'])):?> mimeType="<?php echo $param['representation']['mimeType']; ?>"<?php endif;?><?php if (isset($param['representation']['codecs'])):?> codecs="<?php echo $param['representation']['codecs']; ?>"<?php endif;?><?php if (isset($param['representation']['width'])):?> width="<?php echo $param['representation']['width']; ?>"<?php endif;?><?php if (isset($param['representation']['height'])):?> height="<?php echo $param['representation']['height']; ?>"<?php endif;?><?php if (isset($param['representation']['sar'])):?> sar="<?php echo $param['representation']['sar']; ?>"<?php endif;?><?php if (isset($param['representation']['audioSamplingRate'])):?> audioSamplingRate="<?php echo $param['representation']['audioSamplingRate']; ?>"<?php endif;?>>
|
||||
<?php if ($param['contentType'] === 'audio'):?>
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" />
|
||||
<?php endif;?>
|
||||
<SegmentTemplate
|
||||
<?php if (isset($param['representation']['segmentTemplate']['timescale'] )):?> timescale="<?php echo ['representation']['segmentTemplate']['timescale'] ; ?>" <?php endif;?>
|
||||
<?php if (isset($param['representation']['segmentTemplate']['initialization'] )):?> initialization="<?php echo ['representation']['segmentTemplate']['initialization'] ; ?>" <?php endif;?>
|
||||
<?php if (isset($param['representation']['segmentTemplate']['media'] )):?> media="<?php echo ['representation']['segmentTemplate']['media'] ; ?>" <?php endif;?>
|
||||
<?php if (isset($param['representation']['segmentTemplate']['startNumber'] )):?> startNumber="<?php echo ['representation']['segmentTemplate']['startNumber'] ; ?>" <?php endif;?>
|
||||
>
|
||||
<SegmentTimeline>
|
||||
<?php foreach ($param['representation']['segmentTemplate']['segmentTimeline']['S'] as $s): ?>
|
||||
<S
|
||||
<?php if (isset($s['@attributes']['t'])):?> t="<?php echo $s['@attributes']['t']; ?> " <?php endif;?>
|
||||
<?php if (isset($s['@attributes']['d'])):?> d="<?php echo $s['@attributes']['d']; ?>" <?php endif;?>
|
||||
/>
|
||||
<?php endforeach; ?>
|
||||
</SegmentTimeline>
|
||||
</SegmentTemplate>
|
||||
<Initialization sourceURL="<?php if (isset($param['representation']['SegmentList']['Initialization'])):?><?php echo $param['representation']['SegmentList']['Initialization']; ?><?php endif;?>"/>
|
||||
<?php foreach ($param['representation']['SegmentList']['media'] as $segment): ?>
|
||||
<SegmentURL media="<?php if (isset($segment)):?><?php echo $segment; ?><?php endif;?>"/>
|
||||
<?php endforeach; ?>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<?php endforeach; ?>
|
||||
|
||||
+73
-42
@@ -232,11 +232,7 @@ class TranscodingV1 extends Worker
|
||||
}
|
||||
});
|
||||
|
||||
list($general, $metadata) = $this->transcode($profile['stream'], $video, $format, $representation, $subs);
|
||||
if (!empty($metadata)) {
|
||||
$query->setAttribute('metadata', json_encode($metadata));
|
||||
}
|
||||
|
||||
$general = $this->transcode($profile['stream'], $video, $format, $representation, $subs);
|
||||
if (!empty($general)) {
|
||||
foreach ($general as $key => $value) {
|
||||
$query->setAttribute($key, $value);
|
||||
@@ -245,17 +241,40 @@ class TranscodingV1 extends Worker
|
||||
|
||||
if ($profile['stream'] === 'hls') {
|
||||
$m3u8 = $this->parseM3u8($this->outPath . '_' . $representation->getHeight() . 'p.m3u8');
|
||||
foreach ($m3u8['segments'] as $segment) {
|
||||
Authorization::skip(function () use ($segment, $project, $query) {
|
||||
return $this->database->createDocument('videos_renditions_segments', new Document([
|
||||
'renditionId' => $query->getId(),
|
||||
'fileName' => $segment['fileName'],
|
||||
'path' => $this->getVideoDevice($project->getId())->getPath($this->args['videoId']) . '/' . $this->getRenditionName() . '/',
|
||||
'duration' => $segment['duration'],
|
||||
]));
|
||||
});
|
||||
if (!empty($m3u8['segments'])) {
|
||||
foreach ($m3u8['segments'] as $segment) {
|
||||
Authorization::skip(function () use ($segment, $project, $query) {
|
||||
return $this->database->createDocument('videos_renditions_segments', new Document([
|
||||
'renditionId' => $query->getId(),
|
||||
'type' => 0,
|
||||
'fileName' => $segment['fileName'],
|
||||
'path' => $this->getVideoDevice($project->getId())->getPath($this->args['videoId']) . '/' . $this->getRenditionName() . '/',
|
||||
'duration' => $segment['duration'],
|
||||
]));
|
||||
});
|
||||
}
|
||||
}
|
||||
$query->setAttribute('targetDuration', $m3u8['targetDuration']);
|
||||
|
||||
} else {
|
||||
|
||||
$mpd = $this->parseMpd($this->outPath . '.mpd');
|
||||
if (!empty($mpd['segments'])) {
|
||||
foreach ($mpd['segments'] as $segment) {
|
||||
Authorization::skip(function () use ($segment, $project, $query) {
|
||||
return $this->database->createDocument('videos_renditions_segments', new Document([
|
||||
'renditionId' => $query->getId(),
|
||||
'representationId' => $segment['representationId'],
|
||||
'fileName' => $segment['fileName'],
|
||||
'path' => $this->getVideoDevice($project->getId())->getPath($this->args['videoId']) . '/' . $this->getRenditionName() . '/',
|
||||
'isInit' => $segment['isInit'],
|
||||
]));
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!empty($mpd['metadata'])) {
|
||||
$query->setAttribute('metadata', json_encode($mpd['metadata']));
|
||||
}
|
||||
}
|
||||
|
||||
$query->setAttribute('status', self::STATUS_END);
|
||||
@@ -363,16 +382,8 @@ class TranscodingV1 extends Worker
|
||||
->setAdditionalParams($additionalParams)
|
||||
->save($this->outPath);
|
||||
|
||||
$xml = simplexml_load_string(
|
||||
file_get_contents($this->outDir . $this->args['videoId'] . '.mpd')
|
||||
);
|
||||
|
||||
$general = $this->getVideoStreamInfo($dash->metadata()->export(), $representation);
|
||||
|
||||
return [
|
||||
$general,
|
||||
['mpeg-dash' => !empty($xml) ? json_decode(json_encode((array)$xml), true) : []],
|
||||
];
|
||||
return $this->getVideoStreamInfo($dash->metadata()->export(), $representation);
|
||||
;
|
||||
}
|
||||
|
||||
$hls = $video->hls();
|
||||
@@ -380,7 +391,6 @@ class TranscodingV1 extends Worker
|
||||
foreach ($subtitles as $subtitle) {
|
||||
$sub = new HLSSubtitle($subtitle['path'], $subtitle['name'], $subtitle['code']);
|
||||
$sub->default();
|
||||
///$sub->setM3u8Uri($this->getHlsBaseUri() . $this->args['videoId'] . '_subtitles_' . $subtitle['code'] . '.m3u8');
|
||||
$hls->subtitle($sub);
|
||||
}
|
||||
|
||||
@@ -389,32 +399,53 @@ class TranscodingV1 extends Worker
|
||||
->setHlsAllowCache(false)
|
||||
->addRepresentation($representation)
|
||||
->setAdditionalParams($additionalParams)
|
||||
//->setHlsBaseUrl()
|
||||
->save($this->outPath);
|
||||
|
||||
$general = $this->getVideoStreamInfo($hls->metadata()->export(), $representation);
|
||||
return $this->getVideoStreamInfo($hls->metadata()->export(), $representation);
|
||||
}
|
||||
/**
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
private function parseMpd(string $path): array
|
||||
{
|
||||
$segments = [];
|
||||
$metadata = null;
|
||||
$representationId = 0;
|
||||
$handle = fopen($path, "r");
|
||||
if ($handle) {
|
||||
while (($line = fgets($handle)) !== false) {
|
||||
$line = str_replace([",","\r","\n"], "", $line);
|
||||
if (str_contains($line, "contentType=\"audio\"")) {
|
||||
$representationId++;
|
||||
}
|
||||
|
||||
if (!str_contains($line, "SegmentURL") && !str_contains($line, "Initialization")) {
|
||||
$metadata .= $line . PHP_EOL;
|
||||
} else {
|
||||
$segments[] = [
|
||||
'isInit' => str_contains($line, "Initialization") ? 1 : 0,
|
||||
'representationId' => $representationId,
|
||||
'fileName' => str_replace(["<SegmentURL media=\"","<Initialization sourceURL=\"", "\"/>", " "], "", $line),
|
||||
];
|
||||
}
|
||||
}
|
||||
var_dump($segments);
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
$xml = simplexml_load_string($metadata);
|
||||
|
||||
return [
|
||||
$general, []
|
||||
'metadata' => json_decode(json_encode((array)$xml), true),
|
||||
'segments' => $segments
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $nest
|
||||
* @return string
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
private function getHlsBaseUri(bool $nest = true): string
|
||||
{
|
||||
|
||||
$uri = self::BASE_URL_ENDPOINT . '/' . $this->args['videoId'] . '/' . self::STREAM_HLS . '/';
|
||||
|
||||
if (empty($nest)) {
|
||||
return $uri;
|
||||
}
|
||||
|
||||
return $uri . $this->getRenditionName() . '/';
|
||||
}
|
||||
|
||||
private function parseM3u8(string $path): array
|
||||
{
|
||||
$segments = [];
|
||||
|
||||
+2
-2
@@ -50,7 +50,7 @@
|
||||
"utopia-php/cache": "0.6.*",
|
||||
"utopia-php/cli": "0.13.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.17.*",
|
||||
"utopia-php/database": "0.18.*",
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
@@ -68,7 +68,7 @@
|
||||
"chillerlan/php-qrcode": "4.3.3",
|
||||
"adhocore/jwt": "1.1.2",
|
||||
"slickdeals/statsd": "3.1.0",
|
||||
"aminyazdanpanah/php-ffmpeg-video-streaming": "^1.2",
|
||||
"aminyazdanpanah/php-ffmpeg-video-streaming": "1.2.15",
|
||||
"captioning/captioning": "2.*",
|
||||
"squizlabs/php_codesniffer": "^3.6",
|
||||
"ext-simplexml": "*"
|
||||
|
||||
Reference in New Issue
Block a user