Verified Commit 3e9e6f2f authored by Rigel Kent's avatar Rigel Kent
Browse files

migrate Swagger 2.0 spec to OpenAPI 3.0.0

parent 6cb3482c
......@@ -183,9 +183,8 @@ See [ARCHITECTURE.md](/ARCHITECTURE.md) for a more detailed explanation of the a
#### Backend
* REST API:
* Quick Start: [/support/doc/api/quickstart.md](/support/doc/api/quickstart.md)
* Swagger/OpenAPI schema: [/support/doc/api/openapi.yaml](/support/doc/api/openapi.yaml)
* HTML explorer: [/support/doc/api/html/index.html](https://htmlpreview.github.io/?https://github.com/Chocobozzz/PeerTube/blob/develop/support/doc/api/html/index.html)
* OpenAPI 3.0.0 schema: [/support/doc/api/openapi.yaml](/support/doc/api/openapi.yaml)
* HTML explorer: [docs.joinpeertube.org/api.html](http://docs.joinpeertube.org/api.html)
* Servers communicate with each other with [Activity
Pub](https://www.w3.org/TR/activitypub/).
* Each server has its own users who query it (search videos, query where the
......
......@@ -49,14 +49,12 @@
"create-import-video-file-job": "node ./dist/scripts/create-import-video-file-job.js",
"test": "scripty",
"help": "scripty",
"generate-api-doc": "scripty",
"generate-cli-doc": "scripty",
"parse-log": "node ./dist/scripts/parse-log.js",
"prune-storage": "node ./dist/scripts/prune-storage.js",
"optimize-old-videos": "node ./dist/scripts/optimize-old-videos.js",
"postinstall": "cd client && yarn install --pure-lockfile",
"tsc": "tsc",
"spectacle-docs": "node_modules/spectacle-docs/bin/spectacle.js",
"commander": "commander",
"ng": "ng",
"nodemon": "nodemon",
......@@ -72,13 +70,21 @@
},
"husky": {
"hooks": {
"pre-commit": "lint-staged && ./scripts/openapi-peertube-version.sh"
"pre-commit": "./scripts/openapi-peertube-version.sh && lint-staged"
}
},
"lint-staged": {
"*.scss": [
"sass-lint -c client/.sass-lint.yml",
"git add"
],
"support/doc/api/*.yaml": [
"node ./node_modules/swagger-cli/bin/swagger-cli.js validate support/doc/api/openapi.yaml",
"git add"
],
"server/tools/README.md": [
"npm run generate-cli-doc",
"git add"
]
},
"resolutions": {
......@@ -200,8 +206,8 @@
"nodemon": "^1.11.0",
"sass-lint": "^1.12.1",
"source-map-support": "^0.5.0",
"spectacle-docs": "^1.0.2",
"supertest": "^3.0.0",
"swagger-cli": "^2.2.0",
"ts-node": "7.0.1",
"tslint": "^5.7.0",
"tslint-config-standard": "^8.0.1",
......
#!/bin/sh
set -eu
npm run spectacle-docs -- -t support/doc/api/html support/doc/api/openapi.yaml
parameters:
name:
name: name
in: path
required: true
type: string
description: 'The name of the account (chocobozzz or chocobozzz@peertube.cpy.re for example)'
\ No newline at end of file
parameters:
start:
name: start
in: query
required: false
type: number
description: 'Offset'
count:
name: count
in: query
required: false
type: number
description: 'Number of items'
sort:
name: sort
in: query
required: false
type: string
description: 'Sort column (-createdAt for example)'
responses:
emptySuccess:
description: 'Successful operation'
\ No newline at end of file
This diff is collapsed.
$(function() {
// $(document).foundation();
var $sidebar = $('#sidebar');
if ($sidebar.length) {
var $docs = $('#docs');
var $nav = $sidebar.find('nav');
//
// Setup sidebar navigation
var traverse = new Traverse($nav, {
threshold: 10,
barOffset: $sidebar.position().top
});
$nav.on('update.traverse', function(event, element) {
$nav.find('section').removeClass('expand');
var $section = element.parents('section:first');
if ($section.length) {
$section.addClass('expand');
}
});
//
// Bind the drawer layout
var $drawerLayout = $('.drawer-layout'),
$drawer = $drawerLayout.find('.drawer'),
closeDrawer = function() {
$drawer.removeClass('slide-right slide-left');
$drawer.find('.drawer-overlay').remove();
$drawerLayout.removeClass('drawer-open drawer-slide-left-large drawer-slide-right-large');
return false;
};
// Drawer open buttons
$drawerLayout.find('[data-drawer-slide]').click(function(e) {
var $this = $(this),
direction = $this.data('drawer-slide');
$drawerLayout.addClass('drawer-open');
$drawer.addClass('slide-' + direction);
var $overlay = $('<a href="#" class="drawer-overlay"></a>')
$drawer.append($overlay);
$overlay.click(closeDrawer);
return false;
});
// Drawer close buttons
$drawerLayout.find('[data-drawer-close]').click(closeDrawer);
}
});
/**
* Creates a new instance of Traverse.
* @class
* @fires Traverse#init
* @param {Object} element - jQuery object to add the trigger to.
* @param {Object} options - Overrides to the default plugin settings.
*/
function Traverse(element, options) {
this.$element = element;
this.options = $.extend({}, Traverse.defaults, this.$element.data(), options);
this._init();
}
/**
* Default settings for plugin
*/
Traverse.defaults = {
/**
* Amount of time, in ms, the animated scrolling should take between locations.
* @option
* @example 500
*/
animationDuration: 500,
/**
* Animation style to use when scrolling between locations.
* @option
* @example 'ease-in-out'
*/
animationEasing: 'linear',
/**
* Number of pixels to use as a marker for location changes.
* @option
* @example 50
*/
threshold: 50,
/**
* Class applied to the active locations link on the traverse container.
* @option
* @example 'active'
*/
activeClass: 'active',
/**
* Allows the script to manipulate the url of the current page, and if supported, alter the history.
* @option
* @example true
*/
deepLinking: false,
/**
* Number of pixels to offset the scroll of the page on item click if using a sticky nav bar.
* @option
* @example 25
*/
barOffset: 0
};
/**
* Initializes the Traverse plugin and calls functions to get equalizer functioning on load.
* @private
*/
Traverse.prototype._init = function() {
var id = this.$element[0].id, // || Foundation.GetYoDigits(6, 'traverse'),
_this = this;
this.$targets = $('[data-traverse-target]');
this.$links = this.$element.find('a');
this.$element.attr({
'data-resize': id,
'data-scroll': id,
'id': id
});
this.$active = $();
this.scrollPos = parseInt(window.pageYOffset, 10);
this._events();
};
/**
* Calculates an array of pixel values that are the demarcation lines between locations on the page.
* Can be invoked if new elements are added or the size of a location changes.
* @function
*/
Traverse.prototype.calcPoints = function(){
var _this = this,
body = document.body,
html = document.documentElement;
this.points = [];
this.winHeight = Math.round(Math.max(window.innerHeight, html.clientHeight));
this.docHeight = Math.round(Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight));
this.$targets.each(function(){
var $tar = $(this),
pt = $tar.offset().top; // Math.round($tar.offset().top - _this.options.threshold);
$tar.targetPoint = pt;
_this.points.push(pt);
});
};
/**
* Initializes events for Traverse.
* @private
*/
Traverse.prototype._events = function() {
var _this = this,
$body = $('html, body'),
opts = {
duration: _this.options.animationDuration,
easing: _this.options.animationEasing
};
$(window).one('load', function(){
_this.calcPoints();
_this._updateActive();
$(this).resize(function(e) {
_this.reflow();
}).scroll(function(e) {
_this._updateActive();
});
})
this.$element.on('click', 'a[href^="#"]', function(e) { //'click.zf.traverse'
e.preventDefault();
var arrival = this.getAttribute('href').replace(".", "\\."),
scrollPos = $(arrival).offset().top - _this.options.barOffset; // - _this.options.threshold / 2 - _this.options.barOffset;
$body.stop(true).animate({
scrollTop: scrollPos
}, opts);
});
};
/**
* Calls necessary functions to update Traverse upon DOM change
* @function
*/
Traverse.prototype.reflow = function(){
this.calcPoints();
this._updateActive();
};
/**
* Updates the visibility of an active location link,
* and updates the url hash for the page, if deepLinking enabled.
* @private
* @function
* @fires Traverse#update
*/
Traverse.prototype._updateActive = function(){
var winPos = parseInt(window.pageYOffset, 10),
curIdx;
if(winPos + this.winHeight === this.docHeight){ curIdx = this.points.length - 1; }
else if(winPos < this.points[0]){ curIdx = 0; }
else{
var isDown = this.scrollPos < winPos,
_this = this,
curVisible = this.points.filter(function(p, i){
return isDown ?
p <= (winPos + _this.options.barOffset + _this.options.threshold) :
(p - (_this.options.barOffset + _this.options.threshold)) <= winPos;
// p <= (winPos - (offset - _this.options.threshold)) :
// (p - (-offset + _this.options.threshold)) <= winPos;
});
curIdx = curVisible.length ? curVisible.length - 1 : 0;
}
var $prev = this.$active;
var $next = this.$links.eq(curIdx);
this.$active.removeClass(this.options.activeClass);
this.$active = $next.addClass(this.options.activeClass);
if(this.options.deepLinking){
var hash = this.$active[0].getAttribute('href');
if(window.history.pushState){
window.history.pushState(null, null, hash);
}else{
window.location.hash = hash;
}
}
this.scrollPos = winPos;
// Fire event if the active element was changed
var changed = $prev[0] !== $next[0];
if (changed) {
this.$element.trigger('update.traverse', [this.$active]);
}
};
function Traverse(t,e){this.$element=t,this.options=$.extend({},Traverse.defaults,this.$element.data(),e),this._init()}$(function(){var t=$("#sidebar");if(t.length){$("#docs");var s=t.find("nav");new Traverse(s,{threshold:10,barOffset:t.position().top});s.on("update.traverse",function(t,e){s.find("section").removeClass("expand");var i=e.parents("section:first");i.length&&i.addClass("expand")});var a=$(".drawer-layout"),n=a.find(".drawer"),r=function(){return n.removeClass("slide-right slide-left"),n.find(".drawer-overlay").remove(),a.removeClass("drawer-open drawer-slide-left-large drawer-slide-right-large"),!1};a.find("[data-drawer-slide]").click(function(t){var e=$(this).data("drawer-slide");a.addClass("drawer-open"),n.addClass("slide-"+e);var i=$('<a href="#" class="drawer-overlay"></a>');return n.append(i),i.click(r),!1}),a.find("[data-drawer-close]").click(r)}}),Traverse.defaults={animationDuration:500,animationEasing:"linear",threshold:50,activeClass:"active",deepLinking:!1,barOffset:0},Traverse.prototype._init=function(){var t=this.$element[0].id;this.$targets=$("[data-traverse-target]"),this.$links=this.$element.find("a"),this.$element.attr({"data-resize":t,"data-scroll":t,id:t}),this.$active=$(),this.scrollPos=parseInt(window.pageYOffset,10),this._events()},Traverse.prototype.calcPoints=function(){var i=this,t=document.body,e=document.documentElement;this.points=[],this.winHeight=Math.round(Math.max(window.innerHeight,e.clientHeight)),this.docHeight=Math.round(Math.max(t.scrollHeight,t.offsetHeight,e.clientHeight,e.scrollHeight,e.offsetHeight)),this.$targets.each(function(){var t=$(this),e=t.offset().top;t.targetPoint=e,i.points.push(e)})},Traverse.prototype._events=function(){var s=this,a=$("html, body"),n={duration:s.options.animationDuration,easing:s.options.animationEasing};$(window).one("load",function(){s.calcPoints(),s._updateActive(),$(this).resize(function(t){s.reflow()}).scroll(function(t){s._updateActive()})}),this.$element.on("click",'a[href^="#"]',function(t){t.preventDefault();var e=this.getAttribute("href").replace(".","\\."),i=$(e).offset().top-s.options.barOffset;a.stop(!0).animate({scrollTop:i},n)})},Traverse.prototype.reflow=function(){this.calcPoints(),this._updateActive()},Traverse.prototype._updateActive=function(){var t,i=parseInt(window.pageYOffset,10);if(i+this.winHeight===this.docHeight)t=this.points.length-1;else if(i<this.points[0])t=0;else{var s=this.scrollPos<i,a=this,e=this.points.filter(function(t,e){return s?t<=i+a.options.barOffset+a.options.threshold:t-(a.options.barOffset+a.options.threshold)<=i});t=e.length?e.length-1:0}var n=this.$active,r=this.$links.eq(t);if(this.$active.removeClass(this.options.activeClass),this.$active=r.addClass(this.options.activeClass),this.options.deepLinking){var o=this.$active[0].getAttribute("href");window.history.pushState?window.history.pushState(null,null,o):window.location.hash=o}this.scrollPos=i,n[0]!==r[0]&&this.$element.trigger("update.traverse",[this.$active])};
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
parameters:
id:
name: id
in: path
required: true
type: number
description: 'The user id'
\ No newline at end of file
parameters:
id:
name: id
in: path
required: true
type: string
description: 'The video channel id or uuid'
\ No newline at end of file
parameters:
threadId:
name: threadId
in: path
required: true
type: number
description: 'The thread id (root comment id)'
commentId:
name: threadId
in: path
required: true
type: number
description: 'The comment id'
\ No newline at end of file
parameters:
id:
name: id
in: path
required: true
type: string
description: 'The video id or uuid'
thumbnailfile:
name: thumbnailfile
in: formData
type: file
description: 'Video thumbnail file'
previewfile:
name: previewfile
in: formData
type: file
description: 'Video preview file'
category:
name: category
in: formData
type: number
description: 'Video category'
licence:
name: licence
in: formData
type: number
description: 'Video licence'
language:
name: language
in: formData
type: string
description: 'Video language'
description:
name: description
in: formData
type: string
description: 'Video description'
waitTranscoding:
name: waitTranscoding
in: formData
type: boolean
description: 'Whether or not we wait transcoding before publish the video'
support:
name: support
in: formData
type: string
description: 'Text describing how to support the video uploader'
nsfw:
name: nsfw
in: formData
type: boolean
description: 'Whether or not this video contains sensitive content'
name:
name: name
in: formData
type: string
description: 'Video name'
tags:
name: tags
in: formData
type: string[]
description: 'Video tags'
commentsEnabled:
name: commentsEnabled
in: formData
type: boolean
description: 'Enable or disable comments for this video'
privacy:
name: privacy
in: formData
type: string
enum: [Public, Unlisted]
description: 'Video privacy'
scheduleUpdate:
name: scheduleUpdate
in: formData
required: false
description: 'Schedule an update at a specific datetime'
properties:
updateAt:
type: dateTime
description: 'When to update the video'
required: true
privacy:
type: string
enum: [Public, Unlisted]
description: 'Video privacy target'
......@@ -41,18 +41,18 @@ Uses [JavaScript Standard Style](http://standardjs.com/).
The server is composed by:
* a REST API (Express framework)
* a WebTorrent Tracker
* a REST API (relying on the Express framework) documented on http://docs.joinpeertube.org/api.html
* a WebTorrent Tracker (slightly custom version of [webtorrent/bittorrent-tracker](https://github.com/webtorrent/bittorrent-tracker#server))
A video is seeded by the server with the [WebSeed](http://www.bittorrent.org/beps/bep_0019.html) protocol (HTTP).
![Architecture scheme](/support/doc/development/server/upload-video.png)
When a user uploads a video, the rest API create the torrent file and then adds it to its database.
When a user uploads a video, the REST API creates the torrent file and then adds it to its database.
If a user wants to watch the video, the tracker will indicate all other users that are watching the video + the HTTP url for the WebSeed.
## Newcomers
The server entrypoint is [server.ts](/server.ts). You can begin to look at this file.
Then you can try to understand the [controllers](/server/controllers): they are the entrypoint of each API request.
The server entrypoint is [server.ts](/server.ts). Looking at this file is a good start.
Then you can try to understand the [controllers](/server/controllers): they are the entrypoints of each API request.
This diff is collapsed.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment