Add links to comment mentions
This commit is contained in:
parent
276d03ed1a
commit
e8cb44090e
|
@ -57,6 +57,7 @@
|
||||||
"extract-text-webpack-plugin": "^3.0.2",
|
"extract-text-webpack-plugin": "^3.0.2",
|
||||||
"file-loader": "^1.1.5",
|
"file-loader": "^1.1.5",
|
||||||
"html-webpack-plugin": "^2.19.0",
|
"html-webpack-plugin": "^2.19.0",
|
||||||
|
"linkifyjs": "^2.1.5",
|
||||||
"lodash-es": "^4.17.4",
|
"lodash-es": "^4.17.4",
|
||||||
"markdown-it": "^8.4.0",
|
"markdown-it": "^8.4.0",
|
||||||
"ngx-bootstrap": "2.0.2",
|
"ngx-bootstrap": "2.0.2",
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { getAbsoluteAPIUrl } from '@app/shared/misc/utils'
|
||||||
|
import * as linkify from 'linkifyjs'
|
||||||
|
import * as linkifyHtml from 'linkifyjs/html'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LinkifierService {
|
||||||
|
|
||||||
|
static CLASSNAME = 'linkified'
|
||||||
|
|
||||||
|
private linkifyOptions = {
|
||||||
|
className: {
|
||||||
|
mention: LinkifierService.CLASSNAME + '-mention',
|
||||||
|
url: LinkifierService.CLASSNAME + '-url'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
// Apply plugin
|
||||||
|
this.mentionWithDomainPlugin(linkify)
|
||||||
|
}
|
||||||
|
|
||||||
|
linkify (text: string) {
|
||||||
|
return linkifyHtml(text, this.linkifyOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
private mentionWithDomainPlugin (linkify: any) {
|
||||||
|
const TT = linkify.scanner.TOKENS // Text tokens
|
||||||
|
const { TOKENS: MT, State } = linkify.parser // Multi tokens, state
|
||||||
|
const MultiToken = MT.Base
|
||||||
|
const S_START = linkify.parser.start
|
||||||
|
|
||||||
|
const TT_AT = TT.AT
|
||||||
|
const TT_DOMAIN = TT.DOMAIN
|
||||||
|
const TT_LOCALHOST = TT.LOCALHOST
|
||||||
|
const TT_NUM = TT.NUM
|
||||||
|
const TT_COLON = TT.COLON
|
||||||
|
const TT_SLASH = TT.SLASH
|
||||||
|
const TT_TLD = TT.TLD
|
||||||
|
const TT_UNDERSCORE = TT.UNDERSCORE
|
||||||
|
const TT_DOT = TT.DOT
|
||||||
|
|
||||||
|
function MENTION (value) {
|
||||||
|
this.v = value
|
||||||
|
}
|
||||||
|
|
||||||
|
linkify.inherits(MultiToken, MENTION, {
|
||||||
|
type: 'mentionWithDomain',
|
||||||
|
isLink: true,
|
||||||
|
toHref () {
|
||||||
|
return getAbsoluteAPIUrl() + '/services/redirect/accounts/' + this.toString().substr(1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const S_AT = S_START.jump(TT_AT) // @
|
||||||
|
const S_AT_SYMS = new State()
|
||||||
|
const S_MENTION = new State(MENTION)
|
||||||
|
const S_MENTION_DIVIDER = new State()
|
||||||
|
const S_MENTION_DIVIDER_SYMS = new State()
|
||||||
|
|
||||||
|
// @_,
|
||||||
|
S_AT.on(TT_UNDERSCORE, S_AT_SYMS)
|
||||||
|
|
||||||
|
// @_*
|
||||||
|
S_AT_SYMS
|
||||||
|
.on(TT_UNDERSCORE, S_AT_SYMS)
|
||||||
|
.on(TT_DOT, S_AT_SYMS)
|
||||||
|
|
||||||
|
// Valid mention (not made up entirely of symbols)
|
||||||
|
S_AT
|
||||||
|
.on(TT_DOMAIN, S_MENTION)
|
||||||
|
.on(TT_LOCALHOST, S_MENTION)
|
||||||
|
.on(TT_TLD, S_MENTION)
|
||||||
|
.on(TT_NUM, S_MENTION)
|
||||||
|
|
||||||
|
S_AT_SYMS
|
||||||
|
.on(TT_DOMAIN, S_MENTION)
|
||||||
|
.on(TT_LOCALHOST, S_MENTION)
|
||||||
|
.on(TT_TLD, S_MENTION)
|
||||||
|
.on(TT_NUM, S_MENTION)
|
||||||
|
|
||||||
|
// More valid mentions
|
||||||
|
S_MENTION
|
||||||
|
.on(TT_DOMAIN, S_MENTION)
|
||||||
|
.on(TT_LOCALHOST, S_MENTION)
|
||||||
|
.on(TT_TLD, S_MENTION)
|
||||||
|
.on(TT_COLON, S_MENTION)
|
||||||
|
.on(TT_NUM, S_MENTION)
|
||||||
|
.on(TT_UNDERSCORE, S_MENTION)
|
||||||
|
|
||||||
|
// Mention with a divider
|
||||||
|
S_MENTION
|
||||||
|
.on(TT_AT, S_MENTION_DIVIDER)
|
||||||
|
.on(TT_SLASH, S_MENTION_DIVIDER)
|
||||||
|
.on(TT_DOT, S_MENTION_DIVIDER)
|
||||||
|
|
||||||
|
// Mention _ trailing stash plus syms
|
||||||
|
S_MENTION_DIVIDER.on(TT_UNDERSCORE, S_MENTION_DIVIDER_SYMS)
|
||||||
|
S_MENTION_DIVIDER_SYMS.on(TT_UNDERSCORE, S_MENTION_DIVIDER_SYMS)
|
||||||
|
|
||||||
|
// Once we get a word token, mentions can start up again
|
||||||
|
S_MENTION_DIVIDER
|
||||||
|
.on(TT_DOMAIN, S_MENTION)
|
||||||
|
.on(TT_LOCALHOST, S_MENTION)
|
||||||
|
.on(TT_TLD, S_MENTION)
|
||||||
|
.on(TT_NUM, S_MENTION)
|
||||||
|
|
||||||
|
S_MENTION_DIVIDER_SYMS
|
||||||
|
.on(TT_DOMAIN, S_MENTION)
|
||||||
|
.on(TT_LOCALHOST, S_MENTION)
|
||||||
|
.on(TT_TLD, S_MENTION)
|
||||||
|
.on(TT_NUM, S_MENTION)
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,8 +59,12 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit {
|
||||||
|
|
||||||
if (this.parentComment) {
|
if (this.parentComment) {
|
||||||
const mentions = this.parentComments
|
const mentions = this.parentComments
|
||||||
.filter(c => c.account.id !== this.user.account.id)
|
.filter(c => c.account.id !== this.user.account.id) // Don't add mention of ourselves
|
||||||
.map(c => '@' + c.account.name)
|
.map(c => {
|
||||||
|
if (c.account.host) return '@' + c.account.name + '@' + c.account.host
|
||||||
|
|
||||||
|
return c.account.name
|
||||||
|
})
|
||||||
|
|
||||||
const mentionsSet = new Set(mentions)
|
const mentionsSet = new Set(mentions)
|
||||||
const mentionsText = Array.from(mentionsSet).join(' ') + ' '
|
const mentionsText = Array.from(mentionsSet).join(' ') + ' '
|
||||||
|
|
|
@ -46,10 +46,15 @@
|
||||||
.comment-html {
|
.comment-html {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
|
||||||
a {
|
/deep/ a {
|
||||||
@include disable-default-a-behaviour;
|
@include disable-default-a-behaviour;
|
||||||
|
|
||||||
color: #000;
|
color: #000;
|
||||||
|
|
||||||
|
// Semi bold mentions
|
||||||
|
&:not(.linkified-url) {
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'
|
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'
|
||||||
import { MarkdownService } from '@app/videos/shared'
|
import { LinkifierService } from '@app/videos/+video-watch/comment/linkifier.service'
|
||||||
import * as sanitizeHtml from 'sanitize-html'
|
import * as sanitizeHtml from 'sanitize-html'
|
||||||
import { Account as AccountInterface } from '../../../../../../shared/models/actors'
|
import { Account as AccountInterface } from '../../../../../../shared/models/actors'
|
||||||
import { UserRight } from '../../../../../../shared/models/users'
|
import { UserRight } from '../../../../../../shared/models/users'
|
||||||
|
@ -31,8 +31,8 @@ export class VideoCommentComponent implements OnInit, OnChanges {
|
||||||
newParentComments = []
|
newParentComments = []
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private authService: AuthService,
|
private linkifierService: LinkifierService,
|
||||||
private markdownService: MarkdownService
|
private authService: AuthService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get user () {
|
get user () {
|
||||||
|
@ -93,13 +93,26 @@ export class VideoCommentComponent implements OnInit, OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
private init () {
|
private init () {
|
||||||
this.sanitizedCommentHTML = sanitizeHtml(this.comment.text, {
|
|
||||||
allowedTags: [ 'a', 'p', 'span', 'br' ],
|
|
||||||
allowedSchemes: [ 'http', 'https' ]
|
|
||||||
})
|
|
||||||
|
|
||||||
// Convert possible markdown to html
|
// Convert possible markdown to html
|
||||||
this.sanitizedCommentHTML = this.markdownService.linkify(this.comment.text)
|
const html = this.linkifierService.linkify(this.comment.text)
|
||||||
|
|
||||||
|
this.sanitizedCommentHTML = sanitizeHtml(html, {
|
||||||
|
allowedTags: [ 'a', 'p', 'span', 'br' ],
|
||||||
|
allowedSchemes: [ 'http', 'https' ],
|
||||||
|
allowedAttributes: {
|
||||||
|
'a': [ 'href', 'class' ]
|
||||||
|
},
|
||||||
|
transformTags: {
|
||||||
|
a: (tagName, attribs) => {
|
||||||
|
return {
|
||||||
|
tagName,
|
||||||
|
attribs: Object.assign(attribs, {
|
||||||
|
target: '_blank'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
this.newParentComments = this.parentComments.concat([ this.comment ])
|
this.newParentComments = this.parentComments.concat([ this.comment ])
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
|
import { LinkifierService } from '@app/videos/+video-watch/comment/linkifier.service'
|
||||||
import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-support.component'
|
import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-support.component'
|
||||||
import { TooltipModule } from 'ngx-bootstrap/tooltip'
|
import { TooltipModule } from 'ngx-bootstrap/tooltip'
|
||||||
import { ClipboardModule } from 'ngx-clipboard'
|
import { ClipboardModule } from 'ngx-clipboard'
|
||||||
|
@ -42,6 +43,7 @@ import { VideoWatchComponent } from './video-watch.component'
|
||||||
|
|
||||||
providers: [
|
providers: [
|
||||||
MarkdownService,
|
MarkdownService,
|
||||||
|
LinkifierService,
|
||||||
VideoCommentService
|
VideoCommentService
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,7 +5,6 @@ import * as MarkdownIt from 'markdown-it'
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MarkdownService {
|
export class MarkdownService {
|
||||||
private textMarkdownIt: MarkdownIt.MarkdownIt
|
private textMarkdownIt: MarkdownIt.MarkdownIt
|
||||||
private linkifier: MarkdownIt.MarkdownIt
|
|
||||||
private enhancedMarkdownIt: MarkdownIt.MarkdownIt
|
private enhancedMarkdownIt: MarkdownIt.MarkdownIt
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
|
@ -27,10 +26,6 @@ export class MarkdownService {
|
||||||
.enable('list')
|
.enable('list')
|
||||||
.enable('image')
|
.enable('image')
|
||||||
this.setTargetToLinks(this.enhancedMarkdownIt)
|
this.setTargetToLinks(this.enhancedMarkdownIt)
|
||||||
|
|
||||||
this.linkifier = new MarkdownIt('zero', { linkify: true })
|
|
||||||
.enable('linkify')
|
|
||||||
this.setTargetToLinks(this.linkifier)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
textMarkdownToHTML (markdown: string) {
|
textMarkdownToHTML (markdown: string) {
|
||||||
|
@ -45,12 +40,6 @@ export class MarkdownService {
|
||||||
return this.avoidTruncatedLinks(html)
|
return this.avoidTruncatedLinks(html)
|
||||||
}
|
}
|
||||||
|
|
||||||
linkify (text: string) {
|
|
||||||
const html = this.linkifier.render(text)
|
|
||||||
|
|
||||||
return this.avoidTruncatedLinks(html)
|
|
||||||
}
|
|
||||||
|
|
||||||
private setTargetToLinks (markdownIt: MarkdownIt.MarkdownIt) {
|
private setTargetToLinks (markdownIt: MarkdownIt.MarkdownIt) {
|
||||||
// Snippet from markdown-it documentation: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer
|
// Snippet from markdown-it documentation: https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md#renderer
|
||||||
const defaultRender = markdownIt.renderer.rules.link_open || function (tokens, idx, options, env, self) {
|
const defaultRender = markdownIt.renderer.rules.link_open || function (tokens, idx, options, env, self) {
|
||||||
|
|
|
@ -1526,6 +1526,10 @@ copy-webpack-plugin@4.3.0, copy-webpack-plugin@^4.1.1:
|
||||||
pify "^3.0.0"
|
pify "^3.0.0"
|
||||||
serialize-javascript "^1.4.0"
|
serialize-javascript "^1.4.0"
|
||||||
|
|
||||||
|
core-js@^1.0.0:
|
||||||
|
version "1.2.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||||
|
|
||||||
core-js@^2.4.0, core-js@^2.4.1:
|
core-js@^2.4.0, core-js@^2.4.1:
|
||||||
version "2.5.3"
|
version "2.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
|
||||||
|
@ -2098,6 +2102,12 @@ encodeurl@~1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||||
|
|
||||||
|
encoding@^0.1.11:
|
||||||
|
version "0.1.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
|
||||||
|
dependencies:
|
||||||
|
iconv-lite "~0.4.13"
|
||||||
|
|
||||||
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
|
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
|
||||||
version "1.4.1"
|
version "1.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
|
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
|
||||||
|
@ -2574,6 +2584,18 @@ faye-websocket@~0.11.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
websocket-driver ">=0.5.1"
|
websocket-driver ">=0.5.1"
|
||||||
|
|
||||||
|
fbjs@^0.8.16:
|
||||||
|
version "0.8.16"
|
||||||
|
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
|
||||||
|
dependencies:
|
||||||
|
core-js "^1.0.0"
|
||||||
|
isomorphic-fetch "^2.1.1"
|
||||||
|
loose-envify "^1.0.0"
|
||||||
|
object-assign "^4.1.0"
|
||||||
|
promise "^7.1.1"
|
||||||
|
setimmediate "^1.0.5"
|
||||||
|
ua-parser-js "^0.7.9"
|
||||||
|
|
||||||
figures@^1.3.5:
|
figures@^1.3.5:
|
||||||
version "1.7.0"
|
version "1.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
|
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
|
||||||
|
@ -3269,7 +3291,7 @@ https-browserify@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
|
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
|
||||||
|
|
||||||
iconv-lite@0.4.19:
|
iconv-lite@0.4.19, iconv-lite@~0.4.13:
|
||||||
version "0.4.19"
|
version "0.4.19"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
|
||||||
|
|
||||||
|
@ -3636,7 +3658,7 @@ is-resolvable@^1.0.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
|
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
|
||||||
|
|
||||||
is-stream@^1.1.0:
|
is-stream@^1.0.1, is-stream@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||||
|
|
||||||
|
@ -3684,6 +3706,13 @@ isobject@^3.0.0, isobject@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||||
|
|
||||||
|
isomorphic-fetch@^2.1.1:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
|
||||||
|
dependencies:
|
||||||
|
node-fetch "^1.0.1"
|
||||||
|
whatwg-fetch ">=0.10.0"
|
||||||
|
|
||||||
isstream@~0.1.2:
|
isstream@~0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||||
|
@ -3713,6 +3742,10 @@ istanbul-lib-instrument@^1.7.3:
|
||||||
istanbul-lib-coverage "^1.1.1"
|
istanbul-lib-coverage "^1.1.1"
|
||||||
semver "^5.3.0"
|
semver "^5.3.0"
|
||||||
|
|
||||||
|
jquery@>=1.9.0:
|
||||||
|
version "3.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
|
||||||
|
|
||||||
js-base64@^2.1.8, js-base64@^2.1.9:
|
js-base64@^2.1.8, js-base64@^2.1.9:
|
||||||
version "2.4.3"
|
version "2.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.3.tgz#2e545ec2b0f2957f41356510205214e98fad6582"
|
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.3.tgz#2e545ec2b0f2957f41356510205214e98fad6582"
|
||||||
|
@ -3934,6 +3967,14 @@ linkify-it@^2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
uc.micro "^1.0.1"
|
uc.micro "^1.0.1"
|
||||||
|
|
||||||
|
linkifyjs@^2.1.5:
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-2.1.5.tgz#effc9f01e4aeafbbdbef21a45feab38b9516f93e"
|
||||||
|
optionalDependencies:
|
||||||
|
jquery ">=1.9.0"
|
||||||
|
react ">=0.14.0"
|
||||||
|
react-dom ">=0.14.0"
|
||||||
|
|
||||||
load-ip-set@^1.2.7:
|
load-ip-set@^1.2.7:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/load-ip-set/-/load-ip-set-1.3.1.tgz#cfd050c6916e7ba0ca85d0b566e7854713eb495e"
|
resolved "https://registry.yarnpkg.com/load-ip-set/-/load-ip-set-1.3.1.tgz#cfd050c6916e7ba0ca85d0b566e7854713eb495e"
|
||||||
|
@ -4122,7 +4163,7 @@ longest@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||||
|
|
||||||
loose-envify@^1.0.0:
|
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4546,6 +4587,13 @@ node-abi@^2.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
semver "^5.4.1"
|
semver "^5.4.1"
|
||||||
|
|
||||||
|
node-fetch@^1.0.1:
|
||||||
|
version "1.7.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
|
||||||
|
dependencies:
|
||||||
|
encoding "^0.1.11"
|
||||||
|
is-stream "^1.0.1"
|
||||||
|
|
||||||
node-forge@0.7.1:
|
node-forge@0.7.1:
|
||||||
version "0.7.1"
|
version "0.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.1.tgz#9da611ea08982f4b94206b3beb4cc9665f20c300"
|
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.1.tgz#9da611ea08982f4b94206b3beb4cc9665f20c300"
|
||||||
|
@ -5487,6 +5535,14 @@ promise@^7.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
asap "~2.0.3"
|
asap "~2.0.3"
|
||||||
|
|
||||||
|
prop-types@^15.6.0:
|
||||||
|
version "15.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
|
||||||
|
dependencies:
|
||||||
|
fbjs "^0.8.16"
|
||||||
|
loose-envify "^1.3.1"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
proxy-addr@~2.0.2:
|
proxy-addr@~2.0.2:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec"
|
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec"
|
||||||
|
@ -5665,6 +5721,24 @@ rc@^1.1.6, rc@^1.1.7:
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
|
react-dom@>=0.14.0:
|
||||||
|
version "16.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.2.0.tgz#69003178601c0ca19b709b33a83369fe6124c044"
|
||||||
|
dependencies:
|
||||||
|
fbjs "^0.8.16"
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
prop-types "^15.6.0"
|
||||||
|
|
||||||
|
react@>=0.14.0:
|
||||||
|
version "16.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba"
|
||||||
|
dependencies:
|
||||||
|
fbjs "^0.8.16"
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
prop-types "^15.6.0"
|
||||||
|
|
||||||
read-cache@^1.0.0:
|
read-cache@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
|
resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
|
||||||
|
@ -6253,7 +6327,7 @@ set-value@^2.0.0:
|
||||||
is-plain-object "^2.0.3"
|
is-plain-object "^2.0.3"
|
||||||
split-string "^3.0.1"
|
split-string "^3.0.1"
|
||||||
|
|
||||||
setimmediate@^1.0.4:
|
setimmediate@^1.0.4, setimmediate@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||||
|
|
||||||
|
@ -7079,6 +7153,10 @@ typescript@2.6, typescript@~2.6.2:
|
||||||
version "2.6.2"
|
version "2.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4"
|
||||||
|
|
||||||
|
ua-parser-js@^0.7.9:
|
||||||
|
version "0.7.17"
|
||||||
|
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
|
||||||
|
|
||||||
uc.micro@^1.0.1, uc.micro@^1.0.3:
|
uc.micro@^1.0.1, uc.micro@^1.0.3:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376"
|
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376"
|
||||||
|
@ -7597,6 +7675,10 @@ webtorrent@^0.98.0:
|
||||||
xtend "^4.0.1"
|
xtend "^4.0.1"
|
||||||
zero-fill "^2.2.3"
|
zero-fill "^2.2.3"
|
||||||
|
|
||||||
|
whatwg-fetch@>=0.10.0:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
|
||||||
|
|
||||||
when@~3.6.x:
|
when@~3.6.x:
|
||||||
version "3.6.4"
|
version "3.6.4"
|
||||||
resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e"
|
resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { CONFIG, EMBED_SIZE, PREVIEWS_SIZE } from '../initializers'
|
import { CONFIG, EMBED_SIZE, PREVIEWS_SIZE } from '../initializers'
|
||||||
import { asyncMiddleware, oembedValidator } from '../middlewares'
|
import { asyncMiddleware, oembedValidator } from '../middlewares'
|
||||||
|
import { accountsNameWithHostGetValidator } from '../middlewares/validators'
|
||||||
import { VideoModel } from '../models/video/video'
|
import { VideoModel } from '../models/video/video'
|
||||||
|
|
||||||
const servicesRouter = express.Router()
|
const servicesRouter = express.Router()
|
||||||
|
@ -9,6 +10,10 @@ servicesRouter.use('/oembed',
|
||||||
asyncMiddleware(oembedValidator),
|
asyncMiddleware(oembedValidator),
|
||||||
generateOEmbed
|
generateOEmbed
|
||||||
)
|
)
|
||||||
|
servicesRouter.use('/redirect/accounts/:nameWithHost',
|
||||||
|
asyncMiddleware(accountsNameWithHostGetValidator),
|
||||||
|
redirectToAccountUrl
|
||||||
|
)
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -62,3 +67,7 @@ function generateOEmbed (req: express.Request, res: express.Response, next: expr
|
||||||
|
|
||||||
return res.json(json)
|
return res.json(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function redirectToAccountUrl (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
|
return res.redirect(res.locals.account.Actor.url)
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,16 @@ function isLocalAccountNameExist (name: string, res: Response) {
|
||||||
return isAccountExist(promise, res)
|
return isAccountExist(promise, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isAccountNameWithHostExist (nameWithDomain: string, res: Response) {
|
||||||
|
const [ accountName, host ] = nameWithDomain.split('@')
|
||||||
|
|
||||||
|
let promise: Bluebird<AccountModel>
|
||||||
|
if (!host) promise = AccountModel.loadLocalByName(accountName)
|
||||||
|
else promise = AccountModel.loadLocalByNameAndHost(accountName, host)
|
||||||
|
|
||||||
|
return isAccountExist(promise, res)
|
||||||
|
}
|
||||||
|
|
||||||
async function isAccountExist (p: Bluebird<AccountModel>, res: Response) {
|
async function isAccountExist (p: Bluebird<AccountModel>, res: Response) {
|
||||||
const account = await p
|
const account = await p
|
||||||
|
|
||||||
|
@ -53,5 +63,6 @@ export {
|
||||||
isAccountIdExist,
|
isAccountIdExist,
|
||||||
isLocalAccountNameExist,
|
isLocalAccountNameExist,
|
||||||
isAccountDescriptionValid,
|
isAccountDescriptionValid,
|
||||||
|
isAccountNameWithHostExist,
|
||||||
isAccountNameValid
|
isAccountNameValid
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ const logger = new winston.createLogger({
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
new winston.transports.Console({
|
new winston.transports.Console({
|
||||||
handleExcegiptions: true,
|
handleExceptions: true,
|
||||||
humanReadableUnhandledException: true,
|
humanReadableUnhandledException: true,
|
||||||
format: winston.format.combine(
|
format: winston.format.combine(
|
||||||
timestampFormatter,
|
timestampFormatter,
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { param } from 'express-validator/check'
|
import { param } from 'express-validator/check'
|
||||||
import { isAccountIdExist, isAccountNameValid, isLocalAccountNameExist } from '../../helpers/custom-validators/accounts'
|
import {
|
||||||
|
isAccountIdExist,
|
||||||
|
isAccountNameValid,
|
||||||
|
isAccountNameWithHostExist,
|
||||||
|
isLocalAccountNameExist
|
||||||
|
} from '../../helpers/custom-validators/accounts'
|
||||||
import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
|
import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { areValidationErrors } from './utils'
|
import { areValidationErrors } from './utils'
|
||||||
|
@ -31,9 +36,23 @@ const accountsGetValidator = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const accountsNameWithHostGetValidator = [
|
||||||
|
param('nameWithHost').exists().withMessage('Should have an account name with host'),
|
||||||
|
|
||||||
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
|
logger.debug('Checking accountsNameWithHostGetValidator parameters', { parameters: req.params })
|
||||||
|
|
||||||
|
if (areValidationErrors(req, res)) return
|
||||||
|
if (!await isAccountNameWithHostExist(req.params.nameWithHost, res)) return
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
localAccountValidator,
|
localAccountValidator,
|
||||||
accountsGetValidator
|
accountsGetValidator,
|
||||||
|
accountsNameWithHostGetValidator
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,6 @@ export class AccountModel extends Model<AccountModel> {
|
||||||
static loadLocalByName (name: string) {
|
static loadLocalByName (name: string) {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
name,
|
|
||||||
[ Sequelize.Op.or ]: [
|
[ Sequelize.Op.or ]: [
|
||||||
{
|
{
|
||||||
userId: {
|
userId: {
|
||||||
|
@ -170,8 +169,42 @@ export class AccountModel extends Model<AccountModel> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: ActorModel,
|
||||||
|
required: true,
|
||||||
|
where: {
|
||||||
|
preferredUsername: name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return AccountModel.findOne(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
static loadLocalByNameAndHost (name: string, host: string) {
|
||||||
|
const query = {
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: ActorModel,
|
||||||
|
required: true,
|
||||||
|
where: {
|
||||||
|
preferredUsername: name
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: ServerModel,
|
||||||
|
required: true,
|
||||||
|
where: {
|
||||||
|
host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
return AccountModel.findOne(query)
|
return AccountModel.findOne(query)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue