import { VssueAPI } from 'vssue' ; import axios , { AxiosInstance , AxiosRequestConfig } from 'axios' ; import { buildURL , concatURL , getCleanURL , parseQuery } from '@vssue/utils' ; import { normalizeUser , normalizeIssue , normalizeComment , normalizeReactions , mapReactionName , } from './utils' ; import { ResponseAccessToken , ResponseUser , ResponseIssue , ResponseComment , ResponseLabel , ResponseMarkdown , ResponseReaction , } from './types' ; see https see https see https export default class GiteaV1 implements VssueAPI . Instance { baseURL : string ; owner : string ; repo : string ; labels : Array < string > ; clientId : string ; clientSecret : string ; state : string ; proxy : string | ( ( url : string ) => string ) ; $http : AxiosInstance ; constructor ( { baseURL = 'https://gitea.com' , owner , repo , labels , clientId , clientSecret , state , proxy , } ) { if ( typeof clientSecret === 'undefined' || typeof proxy === 'undefined' ) { throw new Error ( 'clientSecret and proxy is required for Gitea V1' ) ; } this . baseURL = baseURL ; this . owner = owner ; this . repo = repo ; this . labels = labels ; this . clientId = clientId ; this . clientSecret = clientSecret ; this . state = state ; this . proxy = proxy ; this . $http = axios . create ( { baseURL : concatURL ( baseURL , 'api/v1' ) , headers : { Accept : 'application/json' , } , } ) ; } /**
     * The platform api info
     */ get platform ( ) : VssueAPI . Platform { return { name : 'Gitea' , link : this . baseURL , version : 'v1' , meta : { reactable : true , sortable : false , } , } ; } see https redirectAuth ( ) { window . location . href = buildURL ( concatURL ( this . baseURL , 'login/oauth/authorize' ) , { client_id : this . clientId , redirect_uri : window . location . href , response_type : 'code' , state : this . state , } ) ; } see https remarks async handleAuth ( ) { const query = parseQuery ( window . location . search ) ; if ( query . code ) { if ( query . state !== this . state ) { return null ; } const code = decodeURIComponent ( query . code ) ; delete query . code ; delete query . state ; const replaceURL = buildURL ( getCleanURL ( window . location . href ) , query ) + window . location . hash ; window . history . replaceState ( null , '' , replaceURL ) ; const accessToken = await this . getAccessToken ( { code } ) ; return accessToken ; } return null ; } see https async getAccessToken ( { code } ) { const originalURL = concatURL ( this . baseURL , 'login/oauth/access_token' ) ; const proxyURL = typeof this . proxy === 'function' ? this . proxy ( originalURL ) : this . proxy ; const { data } = await this . $http . post < ResponseAccessToken > ( proxyURL , { client_id : this . clientId , client_secret : this . clientSecret , code : code , grant_type : 'authorization_code' , redirect_uri : window . location . href , } ) ; return data . access_token ; } see https async getUser ( { accessToken , } ) { const { data } = await this . $http . get < ResponseUser > ( 'user' , { headers : { Authorization : `bearer ${ accessToken }` } , } ) ; return normalizeUser ( data , this . baseURL ) ; } see https see https async getIssue ( { accessToken , issueId , issueTitle , } ) { const options = { } ; if ( accessToken ) { options . headers = { Authorization : `bearer ${ accessToken }` , } ; } if ( issueId ) { try { options . params = { timestamp : Date . now ( ) , } ; const { data } = await this . $http . get < ResponseIssue > ( `repos/${ this . owner }/${ this . repo }/issues/${ issueId }` , options ) ; return normalizeIssue ( data , this . baseURL , this . owner , this . repo ) ; } catch ( e ) { if ( e . response && e . response . status === 404 ) { return null ; } else { throw e ; } } } else { /**
             * Gitea only supports using label ids to get issues
             */ const allLabels = await this . getLabels ( { accessToken } ) ; const labels = this . labels . filter ( label => allLabels . find ( item => item . name === label ) ) . map ( label => allLabels . find ( item => item . name === label ) ! . id ) ; options . params = { labels , q : issueTitle , timestamp : Date . now ( ) , } ; const { data } = await this . $http . get < ResponseIssue [ ] > ( `repos/${ this . owner }/${ this . repo }/issues` , options ) ; const issue = data . map ( item => normalizeIssue ( item , this . baseURL , this . owner , this . repo ) ) . find ( item => item . title === issueTitle ) ; return issue || null ; } } see https async postIssue ( { accessToken , title , content , } ) { /**
         * Gitea only supports using label ids to create issue
         */ const labels = await Promise . all ( this . labels . map ( label => this . postLabel ( { accessToken , label , } ) ) ) ; const { data } = await this . $http . post < ResponseIssue > ( `repos/${ this . owner }/${ this . repo }/issues` , { title , body : content , labels , } , { headers : { Authorization : `bearer ${ accessToken }` } , } ) ; return normalizeIssue ( data , this . baseURL , this . owner , this . repo ) ; } see https async getComments ( { accessToken , issueId , query : { page = 1 , perPage = 10 , sort = 'desc' } = { } , } ) { const options = { params : { timestamp : Date . now ( ) , } , } ; if ( accessToken ) { options . headers = { Authorization : `bearer ${ accessToken }` , } ; } const response = await this . $http . get < ResponseComment [ ] > ( `repos/${ this . owner }/${ this . repo }/issues/${ issueId }/comments` , options ) ; const commentsRaw = response . data ; const getCommentsMeta = [ ] ; for ( const comment of commentsRaw ) { getCommentsMeta . push ( ( async ( ) : Promise < void > => { comment . body_html = await this . getMarkdownContent ( { accessToken : accessToken , contentRaw : comment . body , } ) ; } ) ( ) ) ; getCommentsMeta . push ( ( async ( ) : Promise < void > => { comment . reactions = await this . getCommentReactions ( { accessToken : accessToken , issueId : issueId , commentId : comment . id , } ) ; } ) ( ) ) ; } await Promise . all ( getCommentsMeta ) ; return { count : commentsRaw . length , page : 1 , perPage : 50 , data : commentsRaw . map ( item => normalizeComment ( item , this . baseURL ) ) , } ; } see https async postComment ( { accessToken , issueId , content , } ) { const { data } = await this . $http . post < ResponseComment > ( `repos/${ this . owner }/${ this . repo }/issues/${ issueId }/comments` , { body : content , } , { headers : { Authorization : `bearer ${ accessToken }` } , } ) ; return normalizeComment ( data , this . baseURL ) ; } see https async putComment ( { accessToken , issueId , commentId , content , } ) { const { data } = await this . $http . patch < ResponseComment > ( `repos/${ this . owner }/${ this . repo }/issues/comments/${ commentId }` , { body : content , } , { headers : { Authorization : `bearer ${ accessToken }` } , } ) ; const [ contentHTML , reactions ] = await Promise . all ( [ this . getMarkdownContent ( { accessToken : accessToken , contentRaw : data . body , } ) , this . getCommentReactions ( { accessToken : accessToken , issueId : issueId , commentId : data . id , } ) , ] ) ; data . body_html = contentHTML ; data . reactions = reactions ; return normalizeComment ( data , this . baseURL ) ; } see https async deleteComment ( { accessToken , commentId , } ) { const { status } = await this . $http . delete ( `repos/${ this . owner }/${ this . repo }/issues/comments/${ commentId }` , { headers : { Authorization : `bearer ${ accessToken }` } , } ) ; return status === 204 ; } see https async getCommentReactions ( { accessToken , commentId , } ) { const options = { params : { timestamp : Date . now ( ) , } , } ; if ( accessToken ) { options . headers = { Authorization : `bearer ${ accessToken }` , } ; } const { data } = await this . $http . get < ResponseReaction [ ] > ( `repos/${ this . owner }/${ this . repo }/issues/comments/${ commentId }/reactions` , options ) ; return normalizeReactions ( data || [ ] ) ; } see https async postCommentReaction ( { commentId , reaction , accessToken , } ) { const response = await this . $http . post ( `repos/${ this . owner }/${ this . repo }/issues/comments/${ commentId }/reactions` , { content : mapReactionName ( reaction ) , } , { headers : { Authorization : `bearer ${ accessToken }` } , } ) ; if ( response . status === 200 ) { return this . deleteCommentReaction ( { accessToken , commentId , reaction , } ) ; } return response . status === 201 ; } see https async deleteCommentReaction ( { commentId , reaction , accessToken , } ) { const response = await this . $http . request ( { url : `repos/${ this . owner }/${ this . repo }/issues/comments/${ commentId }/reactions` , method : 'delete' , data : { content : mapReactionName ( reaction ) } , headers : { Authorization : `bearer ${ accessToken }` } , } ) ; return response . status === 200 ; } see https async getLabels ( { accessToken , } ) { const options = { params : { timestamp : Date . now ( ) , } , } ; if ( accessToken ) { options . headers = { Authorization : `bearer ${ accessToken }` , } ; } const { data } = await this . $http . get < ResponseLabel [ ] > ( `repos/${ this . owner }/${ this . repo }/labels` , options ) ; return data || [ ] ; } see https async postLabel ( { accessToken , label , color = '#3eaf7c' , description , } ) { const { data } = await this . $http . post < ResponseLabel > ( `repos/${ this . owner }/${ this . repo }/labels` , { name : label , color , description , } , { headers : { Authorization : `bearer ${ accessToken }` } , } ) ; return data . id ; } see https async getMarkdownContent ( { accessToken , contentRaw , } ) { const options = { } ; if ( accessToken ) { options . headers = { Authorization : `bearer ${ accessToken }` , } ; } const { data } = await this . $http . post < ResponseMarkdown > ( `markdown` , { Context : `${ this . owner }/${ this . repo }` , Mode : 'gfm' , Text : contentRaw , Wiki : false , } , options ) ; return data ; } }