Merge branch 'develop' of https://codeberg.org/Daikei/calckey into develop
This commit is contained in:
commit
93444e1944
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "calckey",
|
"name": "calckey",
|
||||||
"version": "13.1.3-rc",
|
"version": "13.2.0-dev",
|
||||||
"codename": "aqua",
|
"codename": "aqua",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<svg id="svg10" version="1.1" sodipodi:docname="title_float.svg" inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="1.95 0.97 167.97 103.23">
|
||||||
|
<sodipodi:namedview id="namedview21" pagecolor="#ffffff" bordercolor="#000000" borderopacity="0.25" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" showgrid="false" inkscape:zoom="1.1507704" inkscape:cx="260.69492" inkscape:cy="102.54" inkscape:window-width="1600" inkscape:window-height="931" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg10"/>
|
||||||
|
<metadata id="metadata16">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||||
|
<dc:title/>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<linearGradient id="myGradient" gradientTransform="rotate(90)">
|
||||||
|
<stop offset="5%" stop-color="#9ccfd8" id="stop5" style="--darkreader-inline-stopcolor: #265760;" data-darkreader-inline-stopcolor=""/>
|
||||||
|
<stop offset="95%" stop-color="#31748f" id="stop7" style="--darkreader-inline-stopcolor: #275d72;" data-darkreader-inline-stopcolor=""/>
|
||||||
|
</linearGradient>
|
||||||
|
<defs id="defs14"/>
|
||||||
|
<g id="g8" fill="url('#myGradient')" word-spacing="0" letter-spacing="0" font-family="OTADESIGN Rounded" font-weight="400">
|
||||||
|
<g id="g17">
|
||||||
|
<g transform="matrix(.26953 0 0 .26953 -55.341 -52.023)" id="g11"/>
|
||||||
|
<g transform="matrix(3.6954 0 0 3.6954 208.34 -284.25)" clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" id="g15">
|
||||||
|
<path d="m -41.8312,77.19 c -3.8683,0 -7.1782,1.3578 -9.9311,4.0734 -2.716,2.7525 -4.0734,6.0628 -4.0734,9.9311 5.0539,0.04979 6.082,0.01348 8.7525,0.0011 0.0024,-4.51e-4 0.0044,-6.05e-4 0.0069,-0.0011 0.03779,-2.8423 2.2103,-4.9346 5.2451,-5.2451 1.4137,0 2.6227,0.52089 3.6268,1.5629 0.855,0.8548 1.897,1.2822 3.1257,1.2822 1.2258,0 2.2676,-0.42741 3.1236,-1.2822 0.85499,-0.85567 1.2833,-1.8976 1.2833,-3.1257 0,-1.2264 -0.42828,-2.2673 -1.2833,-3.1231 -2.7528,-2.7156 -6.0446,-4.0734 -9.8761,-4.0734 z m -5.252,14.006 c -3.4453,-5.5934 -3.4667,0.08539 -8.7525,-0.0011 0,3.8683 1.3584,7.0406 4.0744,9.7931 2.7528,2.7156 6.0623,4.0734 9.9305,4.0734 3.8315,0 7.1238,-1.3578 9.8766,-4.0734 0.85499,-0.85577 1.2827,-1.8967 1.2827,-3.1231 0,-1.2282 -0.42775,-2.2701 -1.2827,-3.1257 -0.85596,-0.8548 -1.8978,-1.2822 -3.1236,-1.2822 -1.2287,0 -2.2707,0.42741 -3.1257,1.2822 -1.0041,1.042 -2.2136,1.5623 -3.6273,1.5623 -3.0348,-0.31051 -5.2084,-2.2633 -5.2462,-5.1056 -0.0024,1.1e-5 -0.0039,-1.2e-5 -0.0063,0 z m 26.154,-7.0965 c -2.8795,0 -5.3538,1.0204 -7.4227,3.0612 -0.64257,0.64316 -0.96404,1.4255 -0.96404,2.3472 0,0.92303 0.32146,1.7062 0.96404,2.3493 0.64331,0.64243 1.4265,0.96351 2.3477,0.96351 0.92347,0 1.7068,-0.32108 2.3493,-0.96351 0.75465,-0.78308 1.6632,-1.1744 2.7256,-1.1744 1.0894,0 2.0261,0.37695 2.8091,1.1316 0.75536,0.78309 1.1332,1.7201 1.1332,2.8102 0,1.0617 -0.39242,1.9703 -1.1754,2.7256 -0.39149,0.4193 -0.86676,0.69884 -1.4249,0.83878 -0.14116,0.02773 -0.25248,0.01369 -0.33614,-0.04175 -0.05605,-0.08456 -0.02751,-0.16827 0.08456,-0.25211 l 0.83825,-0.88053 c 0.64329,-0.64315 0.9651,-1.412 0.9651,-2.306 0,-0.92236 -0.27937,-1.6632 -0.83825,-2.2225 -0.55888,-0.55932 -1.3422,-0.83878 -2.3493,-0.83878 -0.6986,0 -1.397,0.34902 -2.0956,1.0475 l -4.8651,4.8223 c -0.64328,0.6438 -0.96457,1.4271 -0.96457,2.3488 0,0.92302 0.32128,1.7053 0.96457,2.3477 1.9568,1.9293 4.3751,2.8942 7.2546,2.8942 2.9072,0 5.3945,-1.0343 7.4634,-3.103 2.0412,-2.0409 3.0618,-4.501 3.0618,-7.3804 0,-2.9072 -1.0206,-5.3952 -3.0618,-7.4639 -2.0689,-2.0409 -4.5562,-3.0612 -7.4634,-3.0612 z" clip-rule="evenodd" fill-rule="nonzero" stroke-miterlimit="2" stroke-width="0" id="path13" sodipodi:nodetypes="cccccccscsccccccscscscccccccscscscscccccscsccscscsccc"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.2 KiB |
|
@ -0,0 +1,65 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="512.0px"
|
||||||
|
height="512.0px"
|
||||||
|
viewBox="0 0 512.0 512.0"
|
||||||
|
version="1.1"
|
||||||
|
id="SVGRoot"
|
||||||
|
sodipodi:docname="inverse wordmark.svg"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||||
|
id="namedview71"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="0.50150542"
|
||||||
|
inkscape:cx="59.819892"
|
||||||
|
inkscape:cy="189.42966"
|
||||||
|
inkscape:window-width="1600"
|
||||||
|
inkscape:window-height="931"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"><inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid77" /></sodipodi:namedview><defs
|
||||||
|
id="defs66" /><g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"><path
|
||||||
|
id="rect136"
|
||||||
|
style="fill:#31748f;fill-opacity:1;stroke-width:0.996356"
|
||||||
|
d="M 0,0 V 512 H 512 V 0 Z" /><g
|
||||||
|
id="g17"
|
||||||
|
style="font-weight:400;font-family:'OTADESIGN Rounded';letter-spacing:0;word-spacing:0;fill:#ffffff"
|
||||||
|
transform="matrix(2.0847185,0,0,2.0847185,76.820648,146.38203)"><g
|
||||||
|
transform="matrix(0.26953,0,0,0.26953,-55.341,-52.023)"
|
||||||
|
id="g11"
|
||||||
|
style="fill:#ffffff" /><g
|
||||||
|
transform="matrix(3.6954,0,0,3.6954,208.34,-284.25)"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-miterlimit="2"
|
||||||
|
id="g15"
|
||||||
|
style="fill:#ffffff"><path
|
||||||
|
d="m -41.8312,77.19 c -3.8683,0 -7.1782,1.3578 -9.9311,4.0734 -2.716,2.7525 -4.0734,6.0628 -4.0734,9.9311 5.0539,0.04979 6.082,0.01348 8.7525,0.0011 0.0024,-4.51e-4 0.0044,-6.05e-4 0.0069,-0.0011 0.03779,-2.8423 2.2103,-4.9346 5.2451,-5.2451 1.4137,0 2.6227,0.52089 3.6268,1.5629 0.855,0.8548 1.897,1.2822 3.1257,1.2822 1.2258,0 2.2676,-0.42741 3.1236,-1.2822 0.85499,-0.85567 1.2833,-1.8976 1.2833,-3.1257 0,-1.2264 -0.42828,-2.2673 -1.2833,-3.1231 -2.7528,-2.7156 -6.0446,-4.0734 -9.8761,-4.0734 z m -5.252,14.006 c -3.4453,-5.5934 -3.4667,0.08539 -8.7525,-0.0011 0,3.8683 1.3584,7.0406 4.0744,9.7931 2.7528,2.7156 6.0623,4.0734 9.9305,4.0734 3.8315,0 7.1238,-1.3578 9.8766,-4.0734 0.85499,-0.85577 1.2827,-1.8967 1.2827,-3.1231 0,-1.2282 -0.42775,-2.2701 -1.2827,-3.1257 -0.85596,-0.8548 -1.8978,-1.2822 -3.1236,-1.2822 -1.2287,0 -2.2707,0.42741 -3.1257,1.2822 -1.0041,1.042 -2.2136,1.5623 -3.6273,1.5623 -3.0348,-0.31051 -5.2084,-2.2633 -5.2462,-5.1056 -0.0024,1.1e-5 -0.0039,-1.2e-5 -0.0063,0 z m 26.154,-7.0965 c -2.8795,0 -5.3538,1.0204 -7.4227,3.0612 -0.64257,0.64316 -0.96404,1.4255 -0.96404,2.3472 0,0.92303 0.32146,1.7062 0.96404,2.3493 0.64331,0.64243 1.4265,0.96351 2.3477,0.96351 0.92347,0 1.7068,-0.32108 2.3493,-0.96351 0.75465,-0.78308 1.6632,-1.1744 2.7256,-1.1744 1.0894,0 2.0261,0.37695 2.8091,1.1316 0.75536,0.78309 1.1332,1.7201 1.1332,2.8102 0,1.0617 -0.39242,1.9703 -1.1754,2.7256 -0.39149,0.4193 -0.86676,0.69884 -1.4249,0.83878 -0.14116,0.02773 -0.25248,0.01369 -0.33614,-0.04175 -0.05605,-0.08456 -0.02751,-0.16827 0.08456,-0.25211 l 0.83825,-0.88053 c 0.64329,-0.64315 0.9651,-1.412 0.9651,-2.306 0,-0.92236 -0.27937,-1.6632 -0.83825,-2.2225 -0.55888,-0.55932 -1.3422,-0.83878 -2.3493,-0.83878 -0.6986,0 -1.397,0.34902 -2.0956,1.0475 l -4.8651,4.8223 c -0.64328,0.6438 -0.96457,1.4271 -0.96457,2.3488 0,0.92302 0.32128,1.7053 0.96457,2.3477 1.9568,1.9293 4.3751,2.8942 7.2546,2.8942 2.9072,0 5.3945,-1.0343 7.4634,-3.103 2.0412,-2.0409 3.0618,-4.501 3.0618,-7.3804 0,-2.9072 -1.0206,-5.3952 -3.0618,-7.4639 -2.0689,-2.0409 -4.5562,-3.0612 -7.4634,-3.0612 z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill-rule="nonzero"
|
||||||
|
stroke-miterlimit="2"
|
||||||
|
stroke-width="0"
|
||||||
|
id="path13"
|
||||||
|
sodipodi:nodetypes="cccccccscsccccccscscscccccccscscscscccccscsccscscsccc"
|
||||||
|
style="fill:#ffffff" /></g></g></g></svg>
|
After Width: | Height: | Size: 4.3 KiB |
|
@ -197,7 +197,10 @@ export const NoteRepository = db.getRepository(Note).extend({
|
||||||
.map((x) => decodeReaction(x).reaction)
|
.map((x) => decodeReaction(x).reaction)
|
||||||
.map((x) => x.replace(/:/g, ""));
|
.map((x) => x.replace(/:/g, ""));
|
||||||
|
|
||||||
const noteEmoji = await populateEmojis(note.emojis.concat(reactionEmojiNames), host);
|
const noteEmoji = await populateEmojis(
|
||||||
|
note.emojis.concat(reactionEmojiNames),
|
||||||
|
host,
|
||||||
|
);
|
||||||
const reactionEmoji = await populateEmojis(reactionEmojiNames, host);
|
const reactionEmoji = await populateEmojis(reactionEmojiNames, host);
|
||||||
const packed: Packed<"Note"> = await awaitAll({
|
const packed: Packed<"Note"> = await awaitAll({
|
||||||
id: note.id,
|
id: note.id,
|
||||||
|
|
|
@ -161,8 +161,9 @@ export const packedNoteSchema = {
|
||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
emojis: {
|
emojis: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
optional: true, nullable: true,
|
optional: true,
|
||||||
|
nullable: true,
|
||||||
},
|
},
|
||||||
reactions: {
|
reactions: {
|
||||||
type: "object",
|
type: "object",
|
||||||
|
|
|
@ -111,6 +111,16 @@ export async function createNote(
|
||||||
|
|
||||||
const note: IPost = object;
|
const note: IPost = object;
|
||||||
|
|
||||||
|
if (note.id && !note.id.startsWith("https://")) {
|
||||||
|
throw new Error(`unexpected shcema of note.id: ${note.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = getOneApHrefNullable(note.url);
|
||||||
|
|
||||||
|
if (url && !url.startsWith("https://")) {
|
||||||
|
throw new Error(`unexpected shcema of note url: ${url}`);
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`);
|
logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`);
|
||||||
|
|
||||||
logger.info(`Creating the Note: ${note.id}`);
|
logger.info(`Creating the Note: ${note.id}`);
|
||||||
|
@ -140,7 +150,9 @@ export async function createNote(
|
||||||
|
|
||||||
// Skip if author is suspended.
|
// Skip if author is suspended.
|
||||||
if (actor.isSuspended) {
|
if (actor.isSuspended) {
|
||||||
logger.debug(`User ${actor.usernameLower}@${actor.host} suspended; discarding.`)
|
logger.debug(
|
||||||
|
`User ${actor.usernameLower}@${actor.host} suspended; discarding.`,
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,7 +374,7 @@ export async function createNote(
|
||||||
apEmojis,
|
apEmojis,
|
||||||
poll,
|
poll,
|
||||||
uri: note.id,
|
uri: note.id,
|
||||||
url: getOneApHrefNullable(note.url),
|
url: url,
|
||||||
},
|
},
|
||||||
silent,
|
silent,
|
||||||
);
|
);
|
||||||
|
|
|
@ -195,6 +195,12 @@ export async function createPerson(
|
||||||
|
|
||||||
const bday = person["vcard:bday"]?.match(/^\d{4}-\d{2}-\d{2}/);
|
const bday = person["vcard:bday"]?.match(/^\d{4}-\d{2}-\d{2}/);
|
||||||
|
|
||||||
|
const url = getOneApHrefNullable(person.url);
|
||||||
|
|
||||||
|
if (url && !url.startsWith("https://")) {
|
||||||
|
throw new Error(`unexpected shcema of person url: ${url}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Create user
|
// Create user
|
||||||
let user: IRemoteUser;
|
let user: IRemoteUser;
|
||||||
try {
|
try {
|
||||||
|
@ -237,7 +243,7 @@ export async function createPerson(
|
||||||
description: person.summary
|
description: person.summary
|
||||||
? htmlToMfm(truncate(person.summary, summaryLength), person.tag)
|
? htmlToMfm(truncate(person.summary, summaryLength), person.tag)
|
||||||
: null,
|
: null,
|
||||||
url: getOneApHrefNullable(person.url),
|
url: url,
|
||||||
fields,
|
fields,
|
||||||
birthday: bday ? bday[0] : null,
|
birthday: bday ? bday[0] : null,
|
||||||
location: person["vcard:Address"] || null,
|
location: person["vcard:Address"] || null,
|
||||||
|
@ -387,6 +393,12 @@ export async function updatePerson(
|
||||||
|
|
||||||
const bday = person["vcard:bday"]?.match(/^\d{4}-\d{2}-\d{2}/);
|
const bday = person["vcard:bday"]?.match(/^\d{4}-\d{2}-\d{2}/);
|
||||||
|
|
||||||
|
const url = getOneApHrefNullable(person.url);
|
||||||
|
|
||||||
|
if (url && !url.startsWith("https://")) {
|
||||||
|
throw new Error(`unexpected shcema of person url: ${url}`);
|
||||||
|
}
|
||||||
|
|
||||||
const updates = {
|
const updates = {
|
||||||
lastFetchedAt: new Date(),
|
lastFetchedAt: new Date(),
|
||||||
inbox: person.inbox,
|
inbox: person.inbox,
|
||||||
|
@ -430,7 +442,7 @@ export async function updatePerson(
|
||||||
await UserProfiles.update(
|
await UserProfiles.update(
|
||||||
{ userId: exist.id },
|
{ userId: exist.id },
|
||||||
{
|
{
|
||||||
url: getOneApHrefNullable(person.url),
|
url: url,
|
||||||
fields,
|
fields,
|
||||||
description: person.summary
|
description: person.summary
|
||||||
? htmlToMfm(truncate(person.summary, summaryLength), person.tag)
|
? htmlToMfm(truncate(person.summary, summaryLength), person.tag)
|
||||||
|
|
|
@ -198,7 +198,7 @@ import * as ep___i_readAnnouncement from "./endpoints/i/read-announcement.js";
|
||||||
import * as ep___i_regenerateToken from "./endpoints/i/regenerate-token.js";
|
import * as ep___i_regenerateToken from "./endpoints/i/regenerate-token.js";
|
||||||
import * as ep___i_registry_getAll from "./endpoints/i/registry/get-all.js";
|
import * as ep___i_registry_getAll from "./endpoints/i/registry/get-all.js";
|
||||||
import * as ep___i_registry_getDetail from "./endpoints/i/registry/get-detail.js";
|
import * as ep___i_registry_getDetail from "./endpoints/i/registry/get-detail.js";
|
||||||
import * as ep___i_registry_getUnsecure from './endpoints/i/registry/get-unsecure.js';
|
import * as ep___i_registry_getUnsecure from "./endpoints/i/registry/get-unsecure.js";
|
||||||
import * as ep___i_registry_get from "./endpoints/i/registry/get.js";
|
import * as ep___i_registry_get from "./endpoints/i/registry/get.js";
|
||||||
import * as ep___i_registry_keysWithType from "./endpoints/i/registry/keys-with-type.js";
|
import * as ep___i_registry_keysWithType from "./endpoints/i/registry/keys-with-type.js";
|
||||||
import * as ep___i_registry_keys from "./endpoints/i/registry/keys.js";
|
import * as ep___i_registry_keys from "./endpoints/i/registry/keys.js";
|
||||||
|
@ -767,10 +767,10 @@ export interface IEndpointMeta {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEndpoint {
|
export interface IEndpoint {
|
||||||
name: string,
|
name: string;
|
||||||
exec: any, // TODO: may be obosolete @ThatOneCalculator
|
exec: any; // TODO: may be obosolete @ThatOneCalculator
|
||||||
meta: IEndpointMeta,
|
meta: IEndpointMeta;
|
||||||
params: Schema,
|
params: Schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
const endpoints: IEndpoint[] = (eps as [string, any]).map(([name, ep]) => {
|
const endpoints: IEndpoint[] = (eps as [string, any]).map(([name, ep]) => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ApiError } from "../../../error.js";
|
import { ApiError } from "../../../error.js";
|
||||||
import define from "../../../define.js";
|
import define from "../../../define.js";
|
||||||
import { RegistryItems } from "../../../../../models/index.js";
|
import { RegistryItems } from "@/models/index.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
|
@ -7,7 +7,7 @@ import Router from "@koa/router";
|
||||||
import multer from "@koa/multer";
|
import multer from "@koa/multer";
|
||||||
import bodyParser from "koa-bodyparser";
|
import bodyParser from "koa-bodyparser";
|
||||||
import cors from "@koa/cors";
|
import cors from "@koa/cors";
|
||||||
import { apiMastodonCompatible } from './mastodon/ApiMastodonCompatibleService.js';
|
import { apiMastodonCompatible } from "./mastodon/ApiMastodonCompatibleService.js";
|
||||||
import { Instances, AccessTokens, Users } from "@/models/index.js";
|
import { Instances, AccessTokens, Users } from "@/models/index.js";
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import endpoints from "./endpoints.js";
|
import endpoints from "./endpoints.js";
|
||||||
|
@ -19,6 +19,7 @@ import signupPending from "./private/signup-pending.js";
|
||||||
import discord from "./service/discord.js";
|
import discord from "./service/discord.js";
|
||||||
import github from "./service/github.js";
|
import github from "./service/github.js";
|
||||||
import twitter from "./service/twitter.js";
|
import twitter from "./service/twitter.js";
|
||||||
|
import { koaBody } from "koa-body";
|
||||||
|
|
||||||
// Init app
|
// Init app
|
||||||
const app = new Koa();
|
const app = new Koa();
|
||||||
|
@ -35,16 +36,10 @@ app.use(async (ctx, next) => {
|
||||||
await next();
|
await next();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(
|
// Init router
|
||||||
bodyParser({
|
const router = new Router();
|
||||||
// リクエストが multipart/form-data でない限りはJSONだと見なす
|
const mastoRouter = new Router();
|
||||||
detectJSON: (ctx) =>
|
const errorRouter = new Router();
|
||||||
!(
|
|
||||||
ctx.is("multipart/form-data") ||
|
|
||||||
ctx.is("application/x-www-form-urlencoded")
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Init multer instance
|
// Init multer instance
|
||||||
const upload = multer({
|
const upload = multer({
|
||||||
|
@ -55,10 +50,23 @@ const upload = multer({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Init router
|
router.use(
|
||||||
const router = new Router();
|
bodyParser({
|
||||||
|
// リクエストが multipart/form-data でない限りはJSONだと見なす
|
||||||
|
detectJSON: (ctx) =>
|
||||||
|
!(
|
||||||
|
ctx.is("multipart/form-data") ||
|
||||||
|
ctx.is("application/x-www-form-urlencoded")
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
apiMastodonCompatible(router);
|
mastoRouter.use(koaBody({
|
||||||
|
multipart: true,
|
||||||
|
urlencoded: true
|
||||||
|
}));
|
||||||
|
|
||||||
|
apiMastodonCompatible(mastoRouter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register endpoint handlers
|
* Register endpoint handlers
|
||||||
|
@ -144,11 +152,13 @@ router.post("/miauth/:session/check", async (ctx) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Return 404 for unknown API
|
// Return 404 for unknown API
|
||||||
router.all("(.*)", async (ctx) => {
|
errorRouter.all("(.*)", async (ctx) => {
|
||||||
ctx.status = 404;
|
ctx.status = 404;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register router
|
// Register router
|
||||||
|
app.use(mastoRouter.routes());
|
||||||
app.use(router.routes());
|
app.use(router.routes());
|
||||||
|
app.use(errorRouter.routes());
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
|
|
|
@ -1,32 +1,39 @@
|
||||||
import Router from "@koa/router";
|
import Router from "@koa/router";
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
import megalodon, { MegalodonInterface } from "@cutls/megalodon";
|
||||||
import { apiAuthMastodon } from './endpoints/auth.js';
|
import { apiAuthMastodon } from "./endpoints/auth.js";
|
||||||
import { apiAccountMastodon } from './endpoints/account.js';
|
import { apiAccountMastodon } from "./endpoints/account.js";
|
||||||
import { apiStatusMastodon } from './endpoints/status.js';
|
import { apiStatusMastodon } from "./endpoints/status.js";
|
||||||
import { apiFilterMastodon } from './endpoints/filter.js';
|
import { apiFilterMastodon } from "./endpoints/filter.js";
|
||||||
import { apiTimelineMastodon } from './endpoints/timeline.js';
|
import { apiTimelineMastodon } from "./endpoints/timeline.js";
|
||||||
import { apiNotificationsMastodon } from './endpoints/notifications.js';
|
import { apiNotificationsMastodon } from "./endpoints/notifications.js";
|
||||||
import { apiSearchMastodon } from './endpoints/search.js';
|
import { apiSearchMastodon } from "./endpoints/search.js";
|
||||||
import { getInstance } from './endpoints/meta.js';
|
import { getInstance } from "./endpoints/meta.js";
|
||||||
|
|
||||||
export function getClient(BASE_URL: string, authorization: string | undefined): MegalodonInterface {
|
export function getClient(
|
||||||
const accessTokenArr = authorization?.split(' ') ?? [null];
|
BASE_URL: string,
|
||||||
|
authorization: string | undefined,
|
||||||
|
): MegalodonInterface {
|
||||||
|
const accessTokenArr = authorization?.split(" ") ?? [null];
|
||||||
const accessToken = accessTokenArr[accessTokenArr.length - 1];
|
const accessToken = accessTokenArr[accessTokenArr.length - 1];
|
||||||
const generator = (megalodon as any).default
|
const generator = (megalodon as any).default;
|
||||||
const client = generator('misskey', BASE_URL, accessToken) as MegalodonInterface;
|
const client = generator(
|
||||||
return client
|
"misskey",
|
||||||
|
BASE_URL,
|
||||||
|
accessToken,
|
||||||
|
) as MegalodonInterface;
|
||||||
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function apiMastodonCompatible(router: Router): void {
|
export function apiMastodonCompatible(router: Router): void {
|
||||||
apiAuthMastodon(router)
|
apiAuthMastodon(router);
|
||||||
apiAccountMastodon(router)
|
apiAccountMastodon(router);
|
||||||
apiStatusMastodon(router)
|
apiStatusMastodon(router);
|
||||||
apiFilterMastodon(router)
|
apiFilterMastodon(router);
|
||||||
apiTimelineMastodon(router)
|
apiTimelineMastodon(router);
|
||||||
apiNotificationsMastodon(router)
|
apiNotificationsMastodon(router);
|
||||||
apiSearchMastodon(router)
|
apiSearchMastodon(router);
|
||||||
|
|
||||||
router.get('/v1/custom_emojis', async (ctx) => {
|
router.get("/v1/custom_emojis", async (ctx) => {
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
const accessTokens = ctx.request.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -34,13 +41,13 @@ export function apiMastodonCompatible(router: Router): void {
|
||||||
const data = await client.getInstanceCustomEmojis();
|
const data = await client.getInstanceCustomEmojis();
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/v1/instance', async (ctx) => {
|
router.get("/v1/instance", async (ctx) => {
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
const accessTokens = ctx.request.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt
|
const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt
|
||||||
|
@ -49,10 +56,9 @@ export function apiMastodonCompatible(router: Router): void {
|
||||||
const data = await client.getInstance();
|
const data = await client.getInstance();
|
||||||
ctx.body = getInstance(data.data);
|
ctx.body = getInstance(data.data);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +1,56 @@
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
import megalodon, { MegalodonInterface } from "@cutls/megalodon";
|
||||||
import Router from "@koa/router";
|
import Router from "@koa/router";
|
||||||
import { koaBody } from 'koa-body';
|
import { koaBody } from "koa-body";
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js';
|
import { getClient } from "../ApiMastodonCompatibleService.js";
|
||||||
import { toLimitToInt } from './timeline.js';
|
import { toLimitToInt } from "./timeline.js";
|
||||||
|
|
||||||
export function apiAccountMastodon(router: Router): void {
|
export function apiAccountMastodon(router: Router): void {
|
||||||
|
router.get("/v1/accounts/verify_credentials", async (ctx, next) => {
|
||||||
router.get('/v1/accounts/verify_credentials', async (ctx, next) => {
|
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.verifyAccountCredentials();
|
const data = await client.verifyAccountCredentials();
|
||||||
const acct = data.data;
|
const acct = data.data;
|
||||||
acct.url = `${BASE_URL}/@${acct.url}`
|
acct.url = `${BASE_URL}/@${acct.url}`;
|
||||||
acct.note = ''
|
acct.note = "";
|
||||||
acct.avatar_static = acct.avatar
|
acct.avatar_static = acct.avatar;
|
||||||
acct.header = acct.header || ''
|
acct.header = acct.header || "";
|
||||||
acct.header_static = acct.header || ''
|
acct.header_static = acct.header || "";
|
||||||
acct.source = {
|
acct.source = {
|
||||||
note: acct.note,
|
note: acct.note,
|
||||||
fields: acct.fields,
|
fields: acct.fields,
|
||||||
privacy: 'public',
|
privacy: "public",
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
language: ''
|
language: "",
|
||||||
}
|
};
|
||||||
ctx.body = acct
|
ctx.body = acct;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.patch('/v1/accounts/update_credentials', async (ctx) => {
|
router.patch("/v1/accounts/update_credentials", async (ctx) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.updateCredentials((ctx.request as any).body as any);
|
const data = await client.updateCredentials(
|
||||||
|
(ctx.request as any).body as any,
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.get<{ Params: { id: string } }>('/v1/accounts/:id', async (ctx, next) => {
|
router.get<{ Params: { id: string } }>(
|
||||||
|
"/v1/accounts/:id",
|
||||||
|
async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -55,55 +58,76 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
const data = await client.getAccount(ctx.params.id);
|
const data = await client.getAccount(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.get<{ Params: { id: string } }>('/v1/accounts/:id/statuses', async (ctx, next) => {
|
);
|
||||||
|
router.get<{ Params: { id: string } }>(
|
||||||
|
"/v1/accounts/:id/statuses",
|
||||||
|
async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.getAccountStatuses(ctx.params.id, toLimitToInt(ctx.query as any));
|
const data = await client.getAccountStatuses(
|
||||||
|
ctx.params.id,
|
||||||
|
toLimitToInt(ctx.query as any),
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.get<{ Params: { id: string } }>('/v1/accounts/:id/followers', async (ctx, next) => {
|
);
|
||||||
|
router.get<{ Params: { id: string } }>(
|
||||||
|
"/v1/accounts/:id/followers",
|
||||||
|
async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.getAccountFollowers(ctx.params.id, ctx.query as any);
|
const data = await client.getAccountFollowers(
|
||||||
|
ctx.params.id,
|
||||||
|
ctx.query as any,
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.get<{ Params: { id: string } }>('/v1/accounts/:id/following', async (ctx, next) => {
|
);
|
||||||
|
router.get<{ Params: { id: string } }>(
|
||||||
|
"/v1/accounts/:id/following",
|
||||||
|
async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.getAccountFollowing(ctx.params.id, ctx.query as any);
|
const data = await client.getAccountFollowing(
|
||||||
|
ctx.params.id,
|
||||||
|
ctx.query as any,
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.get<{ Params: { id: string } }>('/v1/accounts/:id/lists', async (ctx, next) => {
|
);
|
||||||
|
router.get<{ Params: { id: string } }>(
|
||||||
|
"/v1/accounts/:id/lists",
|
||||||
|
async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -111,13 +135,16 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
const data = await client.getAccountLists(ctx.params.id);
|
const data = await client.getAccountLists(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.post<{ Params: { id: string } }>('/v1/accounts/:id/follow', async (ctx, next) => {
|
);
|
||||||
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/accounts/:id/follow",
|
||||||
|
async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -127,13 +154,16 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
acct.following = true;
|
acct.following = true;
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.post<{ Params: { id: string } }>('/v1/accounts/:id/unfollow', async (ctx, next) => {
|
);
|
||||||
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/accounts/:id/unfollow",
|
||||||
|
async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -143,13 +173,16 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
acct.following = false;
|
acct.following = false;
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.post<{ Params: { id: string } }>('/v1/accounts/:id/block', async (ctx, next) => {
|
);
|
||||||
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/accounts/:id/block",
|
||||||
|
async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -157,13 +190,16 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
const data = await client.blockAccount(ctx.params.id);
|
const data = await client.blockAccount(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.post<{ Params: { id: string } }>('/v1/accounts/:id/unblock', async (ctx, next) => {
|
);
|
||||||
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/accounts/:id/unblock",
|
||||||
|
async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -171,27 +207,36 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
const data = await client.unblockAccount(ctx.params.id);
|
const data = await client.unblockAccount(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.post<{ Params: { id: string } }>('/v1/accounts/:id/mute', async (ctx) => {
|
);
|
||||||
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/accounts/:id/mute",
|
||||||
|
async (ctx) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.muteAccount(ctx.params.id, (ctx.request as any).body as any);
|
const data = await client.muteAccount(
|
||||||
|
ctx.params.id,
|
||||||
|
(ctx.request as any).body as any,
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.post<{ Params: { id: string } }>('/v1/accounts/:id/unmute', async (ctx, next) => {
|
);
|
||||||
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/accounts/:id/unmute",
|
||||||
|
async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -199,43 +244,44 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
const data = await client.unmuteAccount(ctx.params.id);
|
const data = await client.unmuteAccount(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.get('/v1/accounts/relationships', async (ctx, next) => {
|
);
|
||||||
|
router.get("/v1/accounts/relationships", async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const idsRaw = (ctx.query as any)['id[]']
|
const idsRaw = (ctx.query as any)["id[]"];
|
||||||
const ids = typeof idsRaw === 'string' ? [idsRaw] : idsRaw
|
const ids = typeof idsRaw === "string" ? [idsRaw] : idsRaw;
|
||||||
const data = await client.getRelationships(ids) as any;
|
const data = (await client.getRelationships(ids)) as any;
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.get('/v1/bookmarks', async (ctx, next) => {
|
router.get("/v1/bookmarks", async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.getBookmarks(ctx.query as any) as any;
|
const data = (await client.getBookmarks(ctx.query as any)) as any;
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.get('/v1/favourites', async (ctx, next) => {
|
router.get("/v1/favourites", async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -243,13 +289,13 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
const data = await client.getFavourites(ctx.query as any);
|
const data = await client.getFavourites(ctx.query as any);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.get('/v1/mutes', async (ctx, next) => {
|
router.get("/v1/mutes", async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -257,13 +303,13 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
const data = await client.getMutes(ctx.query as any);
|
const data = await client.getMutes(ctx.query as any);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.get('/v1/blocks', async (ctx, next) => {
|
router.get("/v1/blocks", async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -271,27 +317,31 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
const data = await client.getBlocks(ctx.query as any);
|
const data = await client.getBlocks(ctx.query as any);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.get('/v1/follow_ctxs', async (ctx, next) => {
|
router.get("/v1/follow_ctxs", async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.getFollowRequests((ctx.query as any || { limit: 20 }).limit);
|
const data = await client.getFollowRequests(
|
||||||
|
((ctx.query as any) || { limit: 20 }).limit,
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.post<{ Params: { id: string } }>('/v1/follow_ctxs/:id/authorize', async (ctx, next) => {
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/follow_ctxs/:id/authorize",
|
||||||
|
async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -299,13 +349,16 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
const data = await client.acceptFollowRequest(ctx.params.id);
|
const data = await client.acceptFollowRequest(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.post<{ Params: { id: string } }>('/v1/follow_ctxs/:id/reject', async (ctx, next) => {
|
);
|
||||||
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/follow_ctxs/:id/reject",
|
||||||
|
async (ctx, next) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -313,11 +366,11 @@ export function apiAccountMastodon(router: Router): void {
|
||||||
const data = await client.rejectFollowRequest(ctx.params.id);
|
const data = await client.rejectFollowRequest(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status =(401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,81 +1,84 @@
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
import megalodon, { MegalodonInterface } from "@cutls/megalodon";
|
||||||
import Router from "@koa/router";
|
import Router from "@koa/router";
|
||||||
import { koaBody } from 'koa-body';
|
import { koaBody } from "koa-body";
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js';
|
import { getClient } from "../ApiMastodonCompatibleService.js";
|
||||||
|
import bodyParser from "koa-bodyparser";
|
||||||
|
|
||||||
const readScope = [
|
const readScope = [
|
||||||
'read:account',
|
"read:account",
|
||||||
'read:drive',
|
"read:drive",
|
||||||
'read:blocks',
|
"read:blocks",
|
||||||
'read:favorites',
|
"read:favorites",
|
||||||
'read:following',
|
"read:following",
|
||||||
'read:messaging',
|
"read:messaging",
|
||||||
'read:mutes',
|
"read:mutes",
|
||||||
'read:notifications',
|
"read:notifications",
|
||||||
'read:reactions',
|
"read:reactions",
|
||||||
'read:pages',
|
"read:pages",
|
||||||
'read:page-likes',
|
"read:page-likes",
|
||||||
'read:user-groups',
|
"read:user-groups",
|
||||||
'read:channels',
|
"read:channels",
|
||||||
'read:gallery',
|
"read:gallery",
|
||||||
'read:gallery-likes'
|
"read:gallery-likes",
|
||||||
]
|
];
|
||||||
const writeScope = [
|
const writeScope = [
|
||||||
'write:account',
|
"write:account",
|
||||||
'write:drive',
|
"write:drive",
|
||||||
'write:blocks',
|
"write:blocks",
|
||||||
'write:favorites',
|
"write:favorites",
|
||||||
'write:following',
|
"write:following",
|
||||||
'write:messaging',
|
"write:messaging",
|
||||||
'write:mutes',
|
"write:mutes",
|
||||||
'write:notes',
|
"write:notes",
|
||||||
'write:notifications',
|
"write:notifications",
|
||||||
'write:reactions',
|
"write:reactions",
|
||||||
'write:votes',
|
"write:votes",
|
||||||
'write:pages',
|
"write:pages",
|
||||||
'write:page-likes',
|
"write:page-likes",
|
||||||
'write:user-groups',
|
"write:user-groups",
|
||||||
'write:channels',
|
"write:channels",
|
||||||
'write:gallery',
|
"write:gallery",
|
||||||
'write:gallery-likes'
|
"write:gallery-likes",
|
||||||
]
|
];
|
||||||
|
|
||||||
export function apiAuthMastodon(router: Router): void {
|
export function apiAuthMastodon(router: Router): void {
|
||||||
|
router.post("/v1/apps", async (ctx) => {
|
||||||
router.post('/v1/apps', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
const accessTokens = ctx.request.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
const body: any = ctx.request.body;
|
const body: any = ctx.request.body;
|
||||||
try {
|
try {
|
||||||
let scope = body.scopes
|
let scope = body.scopes;
|
||||||
console.log(body)
|
console.log(body);
|
||||||
if (typeof scope === 'string') scope = scope.split(' ')
|
if (typeof scope === "string") scope = scope.split(" ");
|
||||||
const pushScope = new Set<string>()
|
const pushScope = new Set<string>();
|
||||||
for (const s of scope) {
|
for (const s of scope) {
|
||||||
if (s.match(/^read/)) for (const r of readScope) pushScope.add(r)
|
if (s.match(/^read/)) for (const r of readScope) pushScope.add(r);
|
||||||
if (s.match(/^write/)) for (const r of writeScope) pushScope.add(r)
|
if (s.match(/^write/)) for (const r of writeScope) pushScope.add(r);
|
||||||
}
|
}
|
||||||
const scopeArr = Array.from(pushScope)
|
const scopeArr = Array.from(pushScope);
|
||||||
|
|
||||||
let red = body.redirect_uris
|
let red = body.redirect_uris;
|
||||||
if (red === 'urn:ietf:wg:oauth:2.0:oob') {
|
if (red === "urn:ietf:wg:oauth:2.0:oob") {
|
||||||
red = 'https://thedesk.top/hello.html'
|
red = "https://thedesk.top/hello.html";
|
||||||
}
|
}
|
||||||
const appData = await client.registerApp(body.client_name, { scopes: scopeArr, redirect_uris: red, website: body.website });
|
const appData = await client.registerApp(body.client_name, {
|
||||||
|
scopes: scopeArr,
|
||||||
|
redirect_uris: red,
|
||||||
|
website: body.website,
|
||||||
|
});
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
id: appData.id,
|
id: appData.id,
|
||||||
name: appData.name,
|
name: appData.name,
|
||||||
website: appData.website,
|
website: appData.website,
|
||||||
redirect_uri: red,
|
redirect_uri: red,
|
||||||
client_id: Buffer.from(appData.url || '').toString('base64'),
|
client_id: Buffer.from(appData.url || "").toString("base64"),
|
||||||
client_secret: appData.clientSecret,
|
client_secret: appData.clientSecret,
|
||||||
}
|
};
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
import megalodon, { MegalodonInterface } from "@cutls/megalodon";
|
||||||
import Router from "@koa/router";
|
import Router from "@koa/router";
|
||||||
import { koaBody } from 'koa-body';
|
import { getClient } from "../ApiMastodonCompatibleService.js";
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js';
|
|
||||||
|
|
||||||
export function apiFilterMastodon(router: Router): void {
|
export function apiFilterMastodon(router: Router): void {
|
||||||
|
router.get("/v1/filters", async (ctx) => {
|
||||||
router.get('/v1/filters', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
const accessTokens = ctx.request.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -14,13 +12,13 @@ export function apiFilterMastodon(router: Router): void {
|
||||||
const data = await client.getFilters();
|
const data = await client.getFilters();
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/v1/filters/:id', async (ctx) => {
|
router.get("/v1/filters/:id", async (ctx) => {
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
const accessTokens = ctx.request.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -29,13 +27,13 @@ export function apiFilterMastodon(router: Router): void {
|
||||||
const data = await client.getFilter(ctx.params.id);
|
const data = await client.getFilter(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/v1/filters', async (ctx) => {
|
router.post("/v1/filters", async (ctx) => {
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
const accessTokens = ctx.request.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -44,28 +42,32 @@ export function apiFilterMastodon(router: Router): void {
|
||||||
const data = await client.createFilter(body.phrase, body.context, body);
|
const data = await client.createFilter(body.phrase, body.context, body);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/v1/filters/:id', async (ctx) => {
|
router.post("/v1/filters/:id", async (ctx) => {
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
const accessTokens = ctx.request.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
const body: any = ctx.request.body;
|
const body: any = ctx.request.body;
|
||||||
try {
|
try {
|
||||||
const data = await client.updateFilter(ctx.params.id, body.phrase, body.context);
|
const data = await client.updateFilter(
|
||||||
|
ctx.params.id,
|
||||||
|
body.phrase,
|
||||||
|
body.context,
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/v1/filters/:id', async (ctx) => {
|
router.delete("/v1/filters/:id", async (ctx) => {
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
const accessTokens = ctx.request.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -74,10 +76,9 @@ export function apiFilterMastodon(router: Router): void {
|
||||||
const data = await client.deleteFilter(ctx.params.id);
|
const data = await client.deleteFilter(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
import megalodon, { MegalodonInterface } from "@cutls/megalodon";
|
||||||
import Router from "@koa/router";
|
import Router from "@koa/router";
|
||||||
import { koaBody } from 'koa-body';
|
import { koaBody } from "koa-body";
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js';
|
import { getClient } from "../ApiMastodonCompatibleService.js";
|
||||||
import { toTextWithReaction } from './timeline.js';
|
import { toTextWithReaction } from "./timeline.js";
|
||||||
function toLimitToInt(q: any) {
|
function toLimitToInt(q: any) {
|
||||||
if (q.limit) if (typeof q.limit === 'string') q.limit = parseInt(q.limit, 10)
|
if (q.limit) if (typeof q.limit === "string") q.limit = parseInt(q.limit, 10);
|
||||||
return q
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function apiNotificationsMastodon(router: Router): void {
|
export function apiNotificationsMastodon(router: Router): void {
|
||||||
|
router.get("/v1/notifications", async (ctx) => {
|
||||||
router.get('/v1/notifications', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
const accessTokens = ctx.request.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -19,23 +18,26 @@ export function apiNotificationsMastodon(router: Router): void {
|
||||||
const data = await client.getNotifications(toLimitToInt(ctx.query));
|
const data = await client.getNotifications(toLimitToInt(ctx.query));
|
||||||
const notfs = data.data;
|
const notfs = data.data;
|
||||||
const ret = notfs.map((n) => {
|
const ret = notfs.map((n) => {
|
||||||
if(n.type !== 'follow' && n.type !== 'follow_request') {
|
if (n.type !== "follow" && n.type !== "follow_request") {
|
||||||
if (n.type === 'reaction') n.type = 'favourite'
|
if (n.type === "reaction") n.type = "favourite";
|
||||||
n.status = toTextWithReaction(n.status ? [n.status] : [], ctx.hostname)[0]
|
n.status = toTextWithReaction(
|
||||||
return n
|
n.status ? [n.status] : [],
|
||||||
|
ctx.hostname,
|
||||||
|
)[0];
|
||||||
|
return n;
|
||||||
} else {
|
} else {
|
||||||
return n
|
return n;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
ctx.body = ret;
|
ctx.body = ret;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/v1/notification/:id', async (ctx) => {
|
router.get("/v1/notification/:id", async (ctx) => {
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
const accessTokens = ctx.request.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -43,20 +45,20 @@ export function apiNotificationsMastodon(router: Router): void {
|
||||||
try {
|
try {
|
||||||
const dataRaw = await client.getNotification(ctx.params.id);
|
const dataRaw = await client.getNotification(ctx.params.id);
|
||||||
const data = dataRaw.data;
|
const data = dataRaw.data;
|
||||||
if(data.type !== 'follow' && data.type !== 'follow_request') {
|
if (data.type !== "follow" && data.type !== "follow_request") {
|
||||||
if (data.type === 'reaction') data.type = 'favourite'
|
if (data.type === "reaction") data.type = "favourite";
|
||||||
ctx.body = toTextWithReaction([data as any], ctx.request.hostname)[0]
|
ctx.body = toTextWithReaction([data as any], ctx.request.hostname)[0];
|
||||||
} else {
|
} else {
|
||||||
ctx.body = data
|
ctx.body = data;
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/v1/notifications/clear', async (ctx) => {
|
router.post("/v1/notifications/clear", async (ctx) => {
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
const accessTokens = ctx.request.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -65,13 +67,13 @@ export function apiNotificationsMastodon(router: Router): void {
|
||||||
const data = await client.dismissNotifications();
|
const data = await client.dismissNotifications();
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/v1/notification/:id/dismiss', async (ctx) => {
|
router.post("/v1/notification/:id/dismiss", async (ctx) => {
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
const accessTokens = ctx.request.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -80,10 +82,9 @@ export function apiNotificationsMastodon(router: Router): void {
|
||||||
const data = await client.dismissNotification(ctx.params.id);
|
const data = await client.dismissNotification(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,22 @@
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
import megalodon, { MegalodonInterface } from "@cutls/megalodon";
|
||||||
import Router from "@koa/router";
|
import Router from "@koa/router";
|
||||||
import { koaBody } from 'koa-body';
|
import { getClient } from "../ApiMastodonCompatibleService.js";
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js';
|
|
||||||
|
|
||||||
export function apiSearchMastodon(router: Router): void {
|
export function apiSearchMastodon(router: Router): void {
|
||||||
|
router.get("/v1/search", async (ctx) => {
|
||||||
router.get('/v1/search', async (ctx) => {
|
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const accessTokens = ctx.request.headers.authorization;
|
const accessTokens = ctx.request.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
const body: any = ctx.request.body;
|
const body: any = ctx.request.body;
|
||||||
try {
|
try {
|
||||||
const query: any = ctx.query
|
const query: any = ctx.query;
|
||||||
const type = query.type || ''
|
const type = query.type || "";
|
||||||
const data = await client.search(query.q, type, query);
|
const data = await client.search(query.q, type, query);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,54 +1,58 @@
|
||||||
import Router from "@koa/router";
|
import Router from "@koa/router";
|
||||||
import { koaBody } from 'koa-body';
|
import megalodon, { MegalodonInterface } from "@cutls/megalodon";
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
import { getClient } from "../ApiMastodonCompatibleService.js";
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js';
|
import fs from "fs";
|
||||||
import fs from 'fs'
|
import { pipeline } from "node:stream";
|
||||||
import { pipeline } from 'node:stream';
|
import { promisify } from "node:util";
|
||||||
import { promisify } from 'node:util';
|
import { createTemp } from "@/misc/create-temp.js";
|
||||||
import { createTemp } from '@/misc/create-temp.js';
|
import { emojiRegex, emojiRegexAtStartToEnd } from "@/misc/emoji-regex.js";
|
||||||
import { emojiRegex, emojiRegexAtStartToEnd } from '@/misc/emoji-regex.js';
|
import axios from "axios";
|
||||||
import axios from 'axios';
|
|
||||||
const pump = promisify(pipeline);
|
const pump = promisify(pipeline);
|
||||||
|
|
||||||
export function apiStatusMastodon(router: Router): void {
|
export function apiStatusMastodon(router: Router): void {
|
||||||
router.post('/v1/statuses', async (ctx, reply) => {
|
router.post("/v1/statuses", async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const body: any = ctx.request.body
|
const body: any = ctx.request.body;
|
||||||
const text = body.status
|
const text = body.status;
|
||||||
const removed = text.replace(/@\S+/g, '').replaceAll(' ', '')
|
const removed = text.replace(/@\S+/g, "").replaceAll(" ", "");
|
||||||
const isDefaultEmoji = emojiRegexAtStartToEnd.test(removed)
|
const isDefaultEmoji = emojiRegexAtStartToEnd.test(removed);
|
||||||
const isCustomEmoji = /^:[a-zA-Z0-9@_]+:$/.test(removed)
|
const isCustomEmoji = /^:[a-zA-Z0-9@_]+:$/.test(removed);
|
||||||
if (body.in_reply_to_id && isDefaultEmoji || isCustomEmoji) {
|
if ((body.in_reply_to_id && isDefaultEmoji) || isCustomEmoji) {
|
||||||
const a = await client.createEmojiReaction(body.in_reply_to_id, removed)
|
const a = await client.createEmojiReaction(
|
||||||
ctx.body = a.data
|
body.in_reply_to_id,
|
||||||
|
removed,
|
||||||
|
);
|
||||||
|
ctx.body = a.data;
|
||||||
}
|
}
|
||||||
if (body.in_reply_to_id && removed === '/unreact') {
|
if (body.in_reply_to_id && removed === "/unreact") {
|
||||||
try {
|
try {
|
||||||
const id = body.in_reply_to_id
|
const id = body.in_reply_to_id;
|
||||||
const post = await client.getStatus(id)
|
const post = await client.getStatus(id);
|
||||||
const react = post.data.emoji_reactions.filter((e) => e.me)[0].name
|
const react = post.data.emoji_reactions.filter((e) => e.me)[0].name;
|
||||||
const data = await client.deleteEmojiReaction(id, react);
|
const data = await client.deleteEmojiReaction(id, react);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!body.media_ids) body.media_ids = undefined
|
if (!body.media_ids) body.media_ids = undefined;
|
||||||
if (body.media_ids && !body.media_ids.length) body.media_ids = undefined
|
if (body.media_ids && !body.media_ids.length) body.media_ids = undefined;
|
||||||
const data = await client.postStatus(text, body);
|
const data = await client.postStatus(text, body);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.get<{ Params: { id: string } }>('/v1/statuses/:id', async (ctx, reply) => {
|
router.get<{ Params: { id: string } }>(
|
||||||
|
"/v1/statuses/:id",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -56,12 +60,15 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
const data = await client.getStatus(ctx.params.id);
|
const data = await client.getStatus(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.delete<{ Params: { id: string } }>('/v1/statuses/:id', async (ctx, reply) => {
|
);
|
||||||
|
router.delete<{ Params: { id: string } }>(
|
||||||
|
"/v1/statuses/:id",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -69,37 +76,54 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
const data = await client.deleteStatus(ctx.params.id);
|
const data = await client.deleteStatus(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
interface IReaction {
|
interface IReaction {
|
||||||
id: string
|
id: string;
|
||||||
createdAt: string
|
createdAt: string;
|
||||||
user: MisskeyEntity.User,
|
user: MisskeyEntity.User;
|
||||||
type: string
|
type: string;
|
||||||
}
|
}
|
||||||
router.get<{ Params: { id: string } }>('/v1/statuses/:id/context', async (ctx, reply) => {
|
router.get<{ Params: { id: string } }>(
|
||||||
|
"/v1/statuses/:id/context",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const id = ctx.params.id
|
const id = ctx.params.id;
|
||||||
const data = await client.getStatusContext(id, ctx.query as any);
|
const data = await client.getStatusContext(id, ctx.query as any);
|
||||||
const status = await client.getStatus(id);
|
const status = await client.getStatus(id);
|
||||||
const reactionsAxios = await axios.get(`${BASE_URL}/api/notes/reactions?noteId=${id}`)
|
const reactionsAxios = await axios.get(
|
||||||
const reactions: IReaction[] = reactionsAxios.data
|
`${BASE_URL}/api/notes/reactions?noteId=${id}`,
|
||||||
const text = reactions.map((r) => `${r.type.replace('@.', '')} ${r.user.username}`).join('<br />')
|
);
|
||||||
data.data.descendants.unshift(statusModel(status.data.id, status.data.account.id, status.data.emojis, text))
|
const reactions: IReaction[] = reactionsAxios.data;
|
||||||
|
const text = reactions
|
||||||
|
.map((r) => `${r.type.replace("@.", "")} ${r.user.username}`)
|
||||||
|
.join("<br />");
|
||||||
|
data.data.descendants.unshift(
|
||||||
|
statusModel(
|
||||||
|
status.data.id,
|
||||||
|
status.data.account.id,
|
||||||
|
status.data.emojis,
|
||||||
|
text,
|
||||||
|
),
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.get<{ Params: { id: string } }>('/v1/statuses/:id/reblogged_by', async (ctx, reply) => {
|
);
|
||||||
|
router.get<{ Params: { id: string } }>(
|
||||||
|
"/v1/statuses/:id/reblogged_by",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -107,31 +131,43 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
const data = await client.getStatusRebloggedBy(ctx.params.id);
|
const data = await client.getStatusRebloggedBy(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.get<{ Params: { id: string } }>('/v1/statuses/:id/favourited_by', async (ctx, reply) => {
|
);
|
||||||
ctx.body = []
|
router.get<{ Params: { id: string } }>(
|
||||||
});
|
"/v1/statuses/:id/favourited_by",
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/favourite', async (ctx, reply) => {
|
async (ctx, reply) => {
|
||||||
|
ctx.body = [];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/statuses/:id/favourite",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
const react = await getFirstReaction(BASE_URL, accessTokens);
|
const react = await getFirstReaction(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const a = await client.createEmojiReaction(ctx.params.id, react) as any;
|
const a = (await client.createEmojiReaction(
|
||||||
|
ctx.params.id,
|
||||||
|
react,
|
||||||
|
)) as any;
|
||||||
//const data = await client.favouriteStatus(ctx.params.id) as any;
|
//const data = await client.favouriteStatus(ctx.params.id) as any;
|
||||||
ctx.body = a.data;
|
ctx.body = a.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/unfavourite', async (ctx, reply) => {
|
);
|
||||||
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/statuses/:id/unfavourite",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -140,13 +176,16 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
const data = await client.deleteEmojiReaction(ctx.params.id, react);
|
const data = await client.deleteEmojiReaction(ctx.params.id, react);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/reblog', async (ctx, reply) => {
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/statuses/:id/reblog",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -154,13 +193,16 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
const data = await client.reblogStatus(ctx.params.id);
|
const data = await client.reblogStatus(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/unreblog', async (ctx, reply) => {
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/statuses/:id/unreblog",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -168,13 +210,16 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
const data = await client.unreblogStatus(ctx.params.id);
|
const data = await client.unreblogStatus(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/bookmark', async (ctx, reply) => {
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/statuses/:id/bookmark",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -182,27 +227,33 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
const data = await client.bookmarkStatus(ctx.params.id);
|
const data = await client.bookmarkStatus(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/unbookmark', async (ctx, reply) => {
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/statuses/:id/unbookmark",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.unbookmarkStatus(ctx.params.id) as any;
|
const data = (await client.unbookmarkStatus(ctx.params.id)) as any;
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/pin', async (ctx, reply) => {
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/statuses/:id/pin",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -210,13 +261,16 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
const data = await client.pinStatus(ctx.params.id);
|
const data = await client.pinStatus(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.post<{ Params: { id: string } }>('/v1/statuses/:id/unpin', async (ctx, reply) => {
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/statuses/:id/unpin",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -224,19 +278,20 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
const data = await client.unpinStatus(ctx.params.id);
|
const data = await client.unpinStatus(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.post('/v1/media', async (ctx, reply) => {
|
);
|
||||||
|
router.post("/v1/media", async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const multipartData = await ctx.file;
|
const multipartData = await ctx.file;
|
||||||
if (!multipartData) {
|
if (!multipartData) {
|
||||||
ctx.body = { error: 'No image' };
|
ctx.body = { error: "No image" };
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const [path] = await createTemp();
|
const [path] = await createTemp();
|
||||||
|
@ -245,19 +300,19 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
const data = await client.uploadMedia(image);
|
const data = await client.uploadMedia(image);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.post('/v2/media', async (ctx, reply) => {
|
router.post("/v2/media", async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const multipartData = await ctx.file;
|
const multipartData = await ctx.file;
|
||||||
if (!multipartData) {
|
if (!multipartData) {
|
||||||
ctx.body = { error: 'No image' };
|
ctx.body = { error: "No image" };
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const [path] = await createTemp();
|
const [path] = await createTemp();
|
||||||
|
@ -266,12 +321,14 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
const data = await client.uploadMedia(image);
|
const data = await client.uploadMedia(image);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.get<{ Params: { id: string } }>('/v1/media/:id', async (ctx, reply) => {
|
router.get<{ Params: { id: string } }>(
|
||||||
|
"/v1/media/:id",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -279,25 +336,34 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
const data = await client.getMedia(ctx.params.id);
|
const data = await client.getMedia(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.put<{ Params: { id: string } }>('/v1/media/:id', async (ctx, reply) => {
|
);
|
||||||
|
router.put<{ Params: { id: string } }>(
|
||||||
|
"/v1/media/:id",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.updateMedia(ctx.params.id, ctx.request.body as any);
|
const data = await client.updateMedia(
|
||||||
|
ctx.params.id,
|
||||||
|
ctx.request.body as any,
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.get<{ Params: { id: string } }>('/v1/polls/:id', async (ctx, reply) => {
|
);
|
||||||
|
router.get<{ Params: { id: string } }>(
|
||||||
|
"/v1/polls/:id",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -305,68 +371,82 @@ export function apiStatusMastodon(router: Router): void {
|
||||||
const data = await client.getPoll(ctx.params.id);
|
const data = await client.getPoll(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.post<{ Params: { id: string } }>('/v1/polls/:id/votes', async (ctx, reply) => {
|
);
|
||||||
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/polls/:id/votes",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.votePoll(ctx.params.id, (ctx.request.body as any).choices);
|
const data = await client.votePoll(
|
||||||
|
ctx.params.id,
|
||||||
|
(ctx.request.body as any).choices,
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getFirstReaction(BASE_URL: string, accessTokens: string | undefined) {
|
async function getFirstReaction(
|
||||||
const accessTokenArr = accessTokens?.split(' ') ?? [null];
|
BASE_URL: string,
|
||||||
|
accessTokens: string | undefined,
|
||||||
|
) {
|
||||||
|
const accessTokenArr = accessTokens?.split(" ") ?? [null];
|
||||||
const accessToken = accessTokenArr[accessTokenArr.length - 1];
|
const accessToken = accessTokenArr[accessTokenArr.length - 1];
|
||||||
let react = '👍'
|
let react = "👍";
|
||||||
try {
|
try {
|
||||||
const api = await axios.post(`${BASE_URL}/api/i/registry/get-unsecure`, {
|
const api = await axios.post(`${BASE_URL}/api/i/registry/get-unsecure`, {
|
||||||
scope: ['client', 'base'],
|
scope: ["client", "base"],
|
||||||
key: 'reactions',
|
key: "reactions",
|
||||||
i: accessToken
|
i: accessToken,
|
||||||
})
|
});
|
||||||
const reactRaw = api.data
|
const reactRaw = api.data;
|
||||||
react = Array.isArray(reactRaw) ? api.data[0] : '👍'
|
react = Array.isArray(reactRaw) ? api.data[0] : "👍";
|
||||||
console.log(api.data)
|
console.log(api.data);
|
||||||
return react
|
return react;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return react
|
return react;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function statusModel(id: string | null, acctId: string | null, emojis: MastodonEntity.Emoji[], content: string) {
|
export function statusModel(
|
||||||
const now = "1970-01-02T00:00:00.000Z"
|
id: string | null,
|
||||||
|
acctId: string | null,
|
||||||
|
emojis: MastodonEntity.Emoji[],
|
||||||
|
content: string,
|
||||||
|
) {
|
||||||
|
const now = "1970-01-02T00:00:00.000Z";
|
||||||
return {
|
return {
|
||||||
id: '9atm5frjhb',
|
id: "9atm5frjhb",
|
||||||
uri: 'https://http.cat/404', // ""
|
uri: "https://http.cat/404", // ""
|
||||||
url: 'https://http.cat/404', // "",
|
url: "https://http.cat/404", // "",
|
||||||
account: {
|
account: {
|
||||||
id: '9arzuvv0sw',
|
id: "9arzuvv0sw",
|
||||||
username: 'ReactionBot',
|
username: "ReactionBot",
|
||||||
acct: 'ReactionBot',
|
acct: "ReactionBot",
|
||||||
display_name: 'ReactionOfThisPost',
|
display_name: "ReactionOfThisPost",
|
||||||
locked: false,
|
locked: false,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
followers_count: 0,
|
followers_count: 0,
|
||||||
following_count: 0,
|
following_count: 0,
|
||||||
statuses_count: 0,
|
statuses_count: 0,
|
||||||
note: '',
|
note: "",
|
||||||
url: 'https://http.cat/404',
|
url: "https://http.cat/404",
|
||||||
avatar: 'https://http.cat/404',
|
avatar: "https://http.cat/404",
|
||||||
avatar_static: 'https://http.cat/404',
|
avatar_static: "https://http.cat/404",
|
||||||
header: 'https://http.cat/404', // ""
|
header: "https://http.cat/404", // ""
|
||||||
header_static: 'https://http.cat/404', // ""
|
header_static: "https://http.cat/404", // ""
|
||||||
emojis: [],
|
emojis: [],
|
||||||
fields: [],
|
fields: [],
|
||||||
moved: null,
|
moved: null,
|
||||||
|
@ -386,8 +466,8 @@ export function statusModel(id: string | null, acctId: string | null, emojis: Ma
|
||||||
reblogged: false,
|
reblogged: false,
|
||||||
muted: false,
|
muted: false,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
spoiler_text: '',
|
spoiler_text: "",
|
||||||
visibility: 'public' as const,
|
visibility: "public" as const,
|
||||||
media_attachments: [],
|
media_attachments: [],
|
||||||
mentions: [],
|
mentions: [],
|
||||||
tags: [],
|
tags: [],
|
||||||
|
@ -399,5 +479,5 @@ export function statusModel(id: string | null, acctId: string | null, emojis: Ma
|
||||||
emoji_reactions: [],
|
emoji_reactions: [],
|
||||||
bookmarked: false,
|
bookmarked: false,
|
||||||
quote: false,
|
quote: false,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,81 +1,99 @@
|
||||||
import Router from "@koa/router";
|
import Router from "@koa/router";
|
||||||
import { koaBody } from 'koa-body';
|
import megalodon, { Entity, MegalodonInterface } from "@cutls/megalodon";
|
||||||
import megalodon, { Entity, MegalodonInterface } from '@cutls/megalodon';
|
import { getClient } from "../ApiMastodonCompatibleService.js";
|
||||||
import { getClient } from '../ApiMastodonCompatibleService.js'
|
import { statusModel } from "./status.js";
|
||||||
import { statusModel } from './status.js';
|
import Autolinker from "autolinker";
|
||||||
import Autolinker from 'autolinker';
|
|
||||||
import { ParsedUrlQuery } from "querystring";
|
import { ParsedUrlQuery } from "querystring";
|
||||||
|
|
||||||
export function toLimitToInt(q: ParsedUrlQuery) {
|
export function toLimitToInt(q: ParsedUrlQuery) {
|
||||||
if (q.limit) if (typeof q.limit === 'string') q.limit = parseInt(q.limit, 10).toString()
|
if (q.limit)
|
||||||
return q
|
if (typeof q.limit === "string") q.limit = parseInt(q.limit, 10).toString();
|
||||||
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toTextWithReaction(status: Entity.Status[], host: string) {
|
export function toTextWithReaction(status: Entity.Status[], host: string) {
|
||||||
return status.map((t) => {
|
return status.map((t) => {
|
||||||
if (!t) return statusModel(null, null, [], 'no content')
|
if (!t) return statusModel(null, null, [], "no content");
|
||||||
if (!t.emoji_reactions) return t
|
if (!t.emoji_reactions) return t;
|
||||||
if (t.reblog) t.reblog = toTextWithReaction([t.reblog], host)[0]
|
if (t.reblog) t.reblog = toTextWithReaction([t.reblog], host)[0];
|
||||||
const reactions = t.emoji_reactions.map((r) => `${r.name.replace('@.', '')} (${r.count}${r.me ? "* " : ''})`);
|
const reactions = t.emoji_reactions.map(
|
||||||
|
(r) => `${r.name.replace("@.", "")} (${r.count}${r.me ? "* " : ""})`,
|
||||||
|
);
|
||||||
//t.emojis = getEmoji(t.content, host)
|
//t.emojis = getEmoji(t.content, host)
|
||||||
t.content = `<p>${autoLinker(t.content, host)}</p><p>${reactions.join(', ')}</p>`
|
t.content = `<p>${autoLinker(t.content, host)}</p><p>${reactions.join(
|
||||||
return t
|
", ",
|
||||||
})
|
)}</p>`;
|
||||||
|
return t;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
export function autoLinker(input: string, host: string) {
|
export function autoLinker(input: string, host: string) {
|
||||||
return Autolinker.link(input, {
|
return Autolinker.link(input, {
|
||||||
hashtag: 'twitter',
|
hashtag: "twitter",
|
||||||
mention: 'twitter',
|
mention: "twitter",
|
||||||
email: false,
|
email: false,
|
||||||
stripPrefix: false,
|
stripPrefix: false,
|
||||||
replaceFn: function (match) {
|
replaceFn: function (match) {
|
||||||
switch (match.type) {
|
switch (match.type) {
|
||||||
case 'url':
|
case "url":
|
||||||
return true
|
return true;
|
||||||
case 'mention':
|
case "mention":
|
||||||
console.log("Mention: ", match.getMention());
|
console.log("Mention: ", match.getMention());
|
||||||
console.log("Mention Service Name: ", match.getServiceName());
|
console.log("Mention Service Name: ", match.getServiceName());
|
||||||
return `<a href="https://${host}/@${encodeURIComponent(match.getMention())}" target="_blank">@${match.getMention()}</a>`;
|
return `<a href="https://${host}/@${encodeURIComponent(
|
||||||
case 'hashtag':
|
match.getMention(),
|
||||||
|
)}" target="_blank">@${match.getMention()}</a>`;
|
||||||
|
case "hashtag":
|
||||||
console.log("Hashtag: ", match.getHashtag());
|
console.log("Hashtag: ", match.getHashtag());
|
||||||
return `<a href="https://${host}/tags/${encodeURIComponent(match.getHashtag())}" target="_blank">#${match.getHashtag()}</a>`;
|
return `<a href="https://${host}/tags/${encodeURIComponent(
|
||||||
}
|
match.getHashtag(),
|
||||||
return false
|
)}" target="_blank">#${match.getHashtag()}</a>`;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function apiTimelineMastodon(router: Router): void {
|
export function apiTimelineMastodon(router: Router): void {
|
||||||
router.get('/v1/timelines/public', async (ctx, reply) => {
|
router.get("/v1/timelines/public", async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const query: any = ctx.query
|
const query: any = ctx.query;
|
||||||
const data = query.local ? await client.getLocalTimeline(toLimitToInt(query)) : await client.getPublicTimeline(toLimitToInt(query));
|
const data = query.local
|
||||||
|
? await client.getLocalTimeline(toLimitToInt(query))
|
||||||
|
: await client.getPublicTimeline(toLimitToInt(query));
|
||||||
ctx.body = toTextWithReaction(data.data, ctx.hostname);
|
ctx.body = toTextWithReaction(data.data, ctx.hostname);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.get<{ Params: { hashtag: string } }>('/v1/timelines/tag/:hashtag', async (ctx, reply) => {
|
router.get<{ Params: { hashtag: string } }>(
|
||||||
|
"/v1/timelines/tag/:hashtag",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.getTagTimeline(ctx.params.hashtag, toLimitToInt(ctx.query));
|
const data = await client.getTagTimeline(
|
||||||
|
ctx.params.hashtag,
|
||||||
|
toLimitToInt(ctx.query),
|
||||||
|
);
|
||||||
ctx.body = toTextWithReaction(data.data, ctx.hostname);
|
ctx.body = toTextWithReaction(data.data, ctx.hostname);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.get<{ Params: { hashtag: string } }>('/v1/timelines/home', async (ctx, reply) => {
|
);
|
||||||
|
router.get<{ Params: { hashtag: string } }>(
|
||||||
|
"/v1/timelines/home",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -83,41 +101,50 @@ export function apiTimelineMastodon(router: Router): void {
|
||||||
const data = await client.getHomeTimeline(toLimitToInt(ctx.query));
|
const data = await client.getHomeTimeline(toLimitToInt(ctx.query));
|
||||||
ctx.body = toTextWithReaction(data.data, ctx.hostname);
|
ctx.body = toTextWithReaction(data.data, ctx.hostname);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.get<{ Params: { listId: string } }>('/v1/timelines/list/:listId', async (ctx, reply) => {
|
);
|
||||||
|
router.get<{ Params: { listId: string } }>(
|
||||||
|
"/v1/timelines/list/:listId",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.getListTimeline(ctx.params.listId, toLimitToInt(ctx.query));
|
const data = await client.getListTimeline(
|
||||||
|
ctx.params.listId,
|
||||||
|
toLimitToInt(ctx.query),
|
||||||
|
);
|
||||||
ctx.body = toTextWithReaction(data.data, ctx.hostname);
|
ctx.body = toTextWithReaction(data.data, ctx.hostname);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.get('/v1/conversations', async (ctx, reply) => {
|
);
|
||||||
|
router.get("/v1/conversations", async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.getConversationTimeline(toLimitToInt(ctx.query));
|
const data = await client.getConversationTimeline(
|
||||||
|
toLimitToInt(ctx.query),
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.get('/v1/lists', async (ctx, reply) => {
|
router.get("/v1/lists", async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -125,13 +152,15 @@ export function apiTimelineMastodon(router: Router): void {
|
||||||
const data = await client.getLists();
|
const data = await client.getLists();
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.get<{ Params: { id: string } }>('/v1/lists/:id', async (ctx, reply) => {
|
router.get<{ Params: { id: string } }>(
|
||||||
|
"/v1/lists/:id",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -139,13 +168,14 @@ export function apiTimelineMastodon(router: Router): void {
|
||||||
const data = await client.getList(ctx.params.id);
|
const data = await client.getList(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.post('/v1/lists', async (ctx, reply) => {
|
);
|
||||||
|
router.post("/v1/lists", async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -153,13 +183,15 @@ export function apiTimelineMastodon(router: Router): void {
|
||||||
const data = await client.createList((ctx.query as any).title);
|
const data = await client.createList((ctx.query as any).title);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
router.put<{ Params: { id: string } }>('/v1/lists/:id', async (ctx, reply) => {
|
router.put<{ Params: { id: string } }>(
|
||||||
|
"/v1/lists/:id",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -167,13 +199,16 @@ export function apiTimelineMastodon(router: Router): void {
|
||||||
const data = await client.updateList(ctx.params.id, ctx.query as any);
|
const data = await client.updateList(ctx.params.id, ctx.query as any);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.delete<{ Params: { id: string } }>('/v1/lists/:id', async (ctx, reply) => {
|
);
|
||||||
|
router.delete<{ Params: { id: string } }>(
|
||||||
|
"/v1/lists/:id",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
|
@ -181,66 +216,90 @@ export function apiTimelineMastodon(router: Router): void {
|
||||||
const data = await client.deleteList(ctx.params.id);
|
const data = await client.deleteList(ctx.params.id);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.get<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (ctx, reply) => {
|
);
|
||||||
|
router.get<{ Params: { id: string } }>(
|
||||||
|
"/v1/lists/:id/accounts",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.getAccountsInList(ctx.params.id, ctx.query as any);
|
const data = await client.getAccountsInList(
|
||||||
|
ctx.params.id,
|
||||||
|
ctx.query as any,
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.post<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (ctx, reply) => {
|
);
|
||||||
|
router.post<{ Params: { id: string } }>(
|
||||||
|
"/v1/lists/:id/accounts",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.addAccountsToList(ctx.params.id, (ctx.query as any).account_ids);
|
const data = await client.addAccountsToList(
|
||||||
|
ctx.params.id,
|
||||||
|
(ctx.query as any).account_ids,
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
router.delete<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (ctx, reply) => {
|
);
|
||||||
|
router.delete<{ Params: { id: string } }>(
|
||||||
|
"/v1/lists/:id/accounts",
|
||||||
|
async (ctx, reply) => {
|
||||||
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
const BASE_URL = `${ctx.protocol}://${ctx.hostname}`;
|
||||||
const accessTokens = ctx.headers.authorization;
|
const accessTokens = ctx.headers.authorization;
|
||||||
const client = getClient(BASE_URL, accessTokens);
|
const client = getClient(BASE_URL, accessTokens);
|
||||||
try {
|
try {
|
||||||
const data = await client.deleteAccountsFromList(ctx.params.id, (ctx.query as any).account_ids);
|
const data = await client.deleteAccountsFromList(
|
||||||
|
ctx.params.id,
|
||||||
|
(ctx.query as any).account_ids,
|
||||||
|
);
|
||||||
ctx.body = data.data;
|
ctx.body = data.data;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
console.error(e.response.data)
|
console.error(e.response.data);
|
||||||
ctx.status = (401);
|
ctx.status = 401;
|
||||||
ctx.body = e.response.data;
|
ctx.body = e.response.data;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
function escapeHTML(str: string) {
|
function escapeHTML(str: string) {
|
||||||
if (!str) {
|
if (!str) {
|
||||||
return ''
|
return "";
|
||||||
}
|
}
|
||||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''')
|
return str
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
}
|
}
|
||||||
function nl2br(str: string) {
|
function nl2br(str: string) {
|
||||||
if (!str) {
|
if (!str) {
|
||||||
return ''
|
return "";
|
||||||
}
|
}
|
||||||
str = str.replace(/\r\n/g, '<br />')
|
str = str.replace(/\r\n/g, "<br />");
|
||||||
str = str.replace(/(\n|\r)/g, '<br />')
|
str = str.replace(/(\n|\r)/g, "<br />");
|
||||||
return str
|
return str;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ export const initializeStreamingServer = (server: http.Server) => {
|
||||||
|
|
||||||
ws.on("request", async (request) => {
|
ws.on("request", async (request) => {
|
||||||
const q = request.resourceURL.query as ParsedUrlQuery;
|
const q = request.resourceURL.query as ParsedUrlQuery;
|
||||||
const headers = request.httpRequest.headers['sec-websocket-protocol'] || '';
|
const headers = request.httpRequest.headers["sec-websocket-protocol"] || "";
|
||||||
const cred = q.i || q.access_token || headers;
|
const cred = q.i || q.access_token || headers;
|
||||||
const accessToken = cred.toString();
|
const accessToken = cred.toString();
|
||||||
|
|
||||||
|
@ -48,9 +48,17 @@ export const initializeStreamingServer = (server: http.Server) => {
|
||||||
redisClient.on("message", onRedisMessage);
|
redisClient.on("message", onRedisMessage);
|
||||||
const host = `https://${request.host}`;
|
const host = `https://${request.host}`;
|
||||||
const prepareStream = q.stream?.toString();
|
const prepareStream = q.stream?.toString();
|
||||||
console.log('start', q);
|
console.log("start", q);
|
||||||
|
|
||||||
const main = new MainStreamConnection(connection, ev, user, app, host, accessToken, prepareStream);
|
const main = new MainStreamConnection(
|
||||||
|
connection,
|
||||||
|
ev,
|
||||||
|
user,
|
||||||
|
app,
|
||||||
|
host,
|
||||||
|
accessToken,
|
||||||
|
prepareStream,
|
||||||
|
);
|
||||||
|
|
||||||
const intervalId = user
|
const intervalId = user
|
||||||
? setInterval(() => {
|
? setInterval(() => {
|
||||||
|
|
|
@ -20,8 +20,7 @@ import { createTemp } from "@/misc/create-temp.js";
|
||||||
import { publishMainStream } from "@/services/stream.js";
|
import { publishMainStream } from "@/services/stream.js";
|
||||||
import * as Acct from "@/misc/acct.js";
|
import * as Acct from "@/misc/acct.js";
|
||||||
import { envOption } from "@/env.js";
|
import { envOption } from "@/env.js";
|
||||||
import { koaBody } from 'koa-body';
|
import megalodon, { MegalodonInterface } from "@cutls/megalodon";
|
||||||
import megalodon, { MegalodonInterface } from '@cutls/megalodon';
|
|
||||||
import activityPub from "./activitypub.js";
|
import activityPub from "./activitypub.js";
|
||||||
import nodeinfo from "./nodeinfo.js";
|
import nodeinfo from "./nodeinfo.js";
|
||||||
import wellKnown from "./well-known.js";
|
import wellKnown from "./well-known.js";
|
||||||
|
@ -30,6 +29,7 @@ import fileServer from "./file/index.js";
|
||||||
import proxyServer from "./proxy/index.js";
|
import proxyServer from "./proxy/index.js";
|
||||||
import webServer from "./web/index.js";
|
import webServer from "./web/index.js";
|
||||||
import { initializeStreamingServer } from "./api/streaming.js";
|
import { initializeStreamingServer } from "./api/streaming.js";
|
||||||
|
import { koaBody } from "koa-body";
|
||||||
|
|
||||||
export const serverLogger = new Logger("server", "gray", false);
|
export const serverLogger = new Logger("server", "gray", false);
|
||||||
|
|
||||||
|
@ -70,6 +70,11 @@ app.use(mount("/proxy", proxyServer));
|
||||||
|
|
||||||
// Init router
|
// Init router
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
const mastoRouter = new Router();
|
||||||
|
|
||||||
|
mastoRouter.use(koaBody({
|
||||||
|
urlencoded: true
|
||||||
|
}));
|
||||||
|
|
||||||
// Routing
|
// Routing
|
||||||
router.use(activityPub.routes());
|
router.use(activityPub.routes());
|
||||||
|
@ -135,26 +140,42 @@ router.get("/verify-email/:code", async (ctx) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/oauth/authorize", async (ctx) => {
|
mastoRouter.get("/oauth/authorize", async (ctx) => {
|
||||||
const client_id = ctx.request.query.client_id;
|
const client_id = ctx.request.query.client_id;
|
||||||
console.log(ctx.request.req);
|
console.log(ctx.request.req);
|
||||||
ctx.redirect(Buffer.from(client_id?.toString() || '', 'base64').toString());
|
ctx.redirect(Buffer.from(client_id?.toString() || "", "base64").toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post("/oauth/token", async (ctx) => {
|
mastoRouter.post("/oauth/token", async (ctx) => {
|
||||||
const body: any = ctx.request.body;
|
const body: any = ctx.request.body;
|
||||||
|
let client_id: any = ctx.request.query.client_id;
|
||||||
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`;
|
||||||
const generator = (megalodon as any).default;
|
const generator = (megalodon as any).default;
|
||||||
const client = generator('misskey', BASE_URL, null) as MegalodonInterface;
|
const client = generator("misskey", BASE_URL, null) as MegalodonInterface;
|
||||||
const m = body.code.match(/^[a-zA-Z0-9-]+/);
|
let m = null;
|
||||||
if (!m.length) return { error: 'Invalid code' }
|
if (body.code) {
|
||||||
|
m = body.code.match(/^[a-zA-Z0-9-]+/);
|
||||||
|
if (!m.length) {
|
||||||
|
ctx.body = { error: "Invalid code" };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (client_id instanceof Array) {
|
||||||
|
client_id = client_id.toString();;
|
||||||
|
} else if (!client_id) {
|
||||||
|
client_id = null;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const atData = await client.fetchAccessToken(null, body.client_secret, m[0]);
|
const atData = await client.fetchAccessToken(
|
||||||
|
client_id,
|
||||||
|
body.client_secret,
|
||||||
|
m ? m[0] : '',
|
||||||
|
);
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
access_token: atData.accessToken,
|
access_token: atData.accessToken,
|
||||||
token_type: 'Bearer',
|
token_type: "Bearer",
|
||||||
scope: 'read write follow',
|
scope: "read write follow",
|
||||||
created_at: new Date().getTime() / 1000
|
created_at: Math.floor(new Date().getTime() / 1000),
|
||||||
};
|
};
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
@ -164,6 +185,7 @@ router.post("/oauth/token", async (ctx) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register router
|
// Register router
|
||||||
|
app.use(mastoRouter.routes());
|
||||||
app.use(router.routes());
|
app.use(router.routes());
|
||||||
|
|
||||||
app.use(mount(webServer));
|
app.use(mount(webServer));
|
||||||
|
|
|
@ -44,6 +44,23 @@ export const urlPreviewHandler = async (ctx: Koa.Context) => {
|
||||||
|
|
||||||
logger.succ(`Got preview of ${url}: ${summary.title}`);
|
logger.succ(`Got preview of ${url}: ${summary.title}`);
|
||||||
|
|
||||||
|
if (
|
||||||
|
summary.url &&
|
||||||
|
!(summary.url.startsWith("http://") || summary.url.startsWith("https://"))
|
||||||
|
) {
|
||||||
|
throw new Error("unsupported schema included");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
summary.player?.url &&
|
||||||
|
!(
|
||||||
|
summary.player.url.startsWith("http://") ||
|
||||||
|
summary.player.url.startsWith("https://")
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new Error("unsupported schema included");
|
||||||
|
}
|
||||||
|
|
||||||
summary.icon = wrap(summary.icon);
|
summary.icon = wrap(summary.icon);
|
||||||
summary.thumbnail = wrap(summary.thumbnail);
|
summary.thumbnail = wrap(summary.thumbnail);
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
|
@ -67,6 +67,7 @@ const embedId = `embed${Math.random().toString().replace(/\D/,'')}`;
|
||||||
let tweetHeight = $ref(150);
|
let tweetHeight = $ref(150);
|
||||||
|
|
||||||
const requestUrl = new URL(props.url);
|
const requestUrl = new URL(props.url);
|
||||||
|
if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url');
|
||||||
|
|
||||||
if (requestUrl.hostname === 'twitter.com' || requestUrl.hostname === 'mobile.twitter.com') {
|
if (requestUrl.hostname === 'twitter.com' || requestUrl.hostname === 'mobile.twitter.com') {
|
||||||
const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/);
|
const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/);
|
||||||
|
|
|
@ -33,6 +33,7 @@ const props = defineProps<{
|
||||||
|
|
||||||
const self = props.url.startsWith(local);
|
const self = props.url.startsWith(local);
|
||||||
const url = new URL(props.url);
|
const url = new URL(props.url);
|
||||||
|
if (!['http:', 'https:'].includes(url.protocol)) throw new Error('invalid url');
|
||||||
const el = ref();
|
const el = ref();
|
||||||
|
|
||||||
useTooltip(el, (showing) => {
|
useTooltip(el, (showing) => {
|
||||||
|
|
|
@ -377,12 +377,7 @@ export default defineComponent({
|
||||||
|
|
||||||
case "quote": {
|
case "quote": {
|
||||||
if (!this.nowrap) {
|
if (!this.nowrap) {
|
||||||
return [
|
return [h("blockquote", genEl(token.children))];
|
||||||
h(
|
|
||||||
"blockquote",
|
|
||||||
genEl(token.children),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
h(
|
h(
|
||||||
|
|
|
@ -5,10 +5,10 @@ import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { ui } from "@/config";
|
import { ui } from "@/config";
|
||||||
import { unisonReload } from "@/scripts/unison-reload";
|
import { unisonReload } from "@/scripts/unison-reload";
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from "@/store";
|
||||||
import { instance } from '@/instance';
|
import { instance } from "@/instance";
|
||||||
import { host } from '@/config';
|
import { host } from "@/config";
|
||||||
import XTutorial from '@/components/MkTutorialDialog.vue';
|
import XTutorial from "@/components/MkTutorialDialog.vue";
|
||||||
|
|
||||||
export const navbarItemDef = reactive({
|
export const navbarItemDef = reactive({
|
||||||
notifications: {
|
notifications: {
|
||||||
|
@ -152,54 +152,68 @@ export const navbarItemDef = reactive({
|
||||||
title: "help",
|
title: "help",
|
||||||
icon: "ph-question-bold ph-lg",
|
icon: "ph-question-bold ph-lg",
|
||||||
action: (ev) => {
|
action: (ev) => {
|
||||||
os.popupMenu([{
|
os.popupMenu(
|
||||||
|
[
|
||||||
|
{
|
||||||
text: instance.name ?? host,
|
text: instance.name ?? host,
|
||||||
type: 'label',
|
type: "label",
|
||||||
}, {
|
},
|
||||||
type: 'link',
|
{
|
||||||
|
type: "link",
|
||||||
text: i18n.ts.instanceInfo,
|
text: i18n.ts.instanceInfo,
|
||||||
icon: 'ph-info-bold ph-lg',
|
icon: "ph-info-bold ph-lg",
|
||||||
to: '/about',
|
to: "/about",
|
||||||
}, {
|
},
|
||||||
type: 'link',
|
{
|
||||||
|
type: "link",
|
||||||
text: i18n.ts.aboutMisskey,
|
text: i18n.ts.aboutMisskey,
|
||||||
icon: 'ph-lightbulb-bold ph-lg',
|
icon: "ph-lightbulb-bold ph-lg",
|
||||||
to: '/about-calckey',
|
to: "/about-calckey",
|
||||||
}, {
|
},
|
||||||
type: 'link',
|
{
|
||||||
|
type: "link",
|
||||||
text: i18n.ts._apps.apps,
|
text: i18n.ts._apps.apps,
|
||||||
icon: 'ph-device-mobile-bold ph-lg',
|
icon: "ph-device-mobile-bold ph-lg",
|
||||||
to: '/apps',
|
to: "/apps",
|
||||||
}, {
|
},
|
||||||
type: 'button',
|
{
|
||||||
|
type: "button",
|
||||||
action: async () => {
|
action: async () => {
|
||||||
defaultStore.set('tutorial', 0);
|
defaultStore.set("tutorial", 0);
|
||||||
os.popup(XTutorial, {}, {}, 'closed');
|
os.popup(XTutorial, {}, {}, "closed");
|
||||||
},
|
},
|
||||||
text: i18n.ts.replayTutorial,
|
text: i18n.ts.replayTutorial,
|
||||||
icon: 'ph-circle-wavy-question-bold ph-lg',
|
icon: "ph-circle-wavy-question-bold ph-lg",
|
||||||
}, null, {
|
|
||||||
type: 'parent',
|
|
||||||
text: i18n.ts.developer,
|
|
||||||
icon: 'ph-code-bold ph-lg',
|
|
||||||
children: [{
|
|
||||||
type: 'link',
|
|
||||||
to: '/api-console',
|
|
||||||
text: 'API Console',
|
|
||||||
icon: 'ph-terminal-window-bold ph-lg',
|
|
||||||
}, {
|
|
||||||
text: i18n.ts.document,
|
|
||||||
icon: 'ph-file-doc-bold ph-lg',
|
|
||||||
action: () => {
|
|
||||||
window.open('/api-doc', '_blank');
|
|
||||||
},
|
},
|
||||||
}, {
|
null,
|
||||||
type: 'link',
|
{
|
||||||
to: '/scratchpad',
|
type: "parent",
|
||||||
text: 'AiScript Scratchpad',
|
text: i18n.ts.developer,
|
||||||
icon: 'ph-scribble-loop-bold ph-lg',
|
icon: "ph-code-bold ph-lg",
|
||||||
}]
|
children: [
|
||||||
}], ev.currentTarget ?? ev.target,
|
{
|
||||||
|
type: "link",
|
||||||
|
to: "/api-console",
|
||||||
|
text: "API Console",
|
||||||
|
icon: "ph-terminal-window-bold ph-lg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: i18n.ts.document,
|
||||||
|
icon: "ph-file-doc-bold ph-lg",
|
||||||
|
action: () => {
|
||||||
|
window.open("/api-doc", "_blank");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "link",
|
||||||
|
to: "/scratchpad",
|
||||||
|
text: "AiScript Scratchpad",
|
||||||
|
icon: "ph-scribble-loop-bold ph-lg",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
ev.currentTarget ?? ev.target,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -80,6 +80,8 @@ export default defineComponent({
|
||||||
this.state = 'accepted';
|
this.state = 'accepted';
|
||||||
const getUrlParams = () => window.location.search.substring(1).split('&').reduce((result, query) => { const [k, v] = query.split('='); result[k] = decodeURI(v); return result; }, {});
|
const getUrlParams = () => window.location.search.substring(1).split('&').reduce((result, query) => { const [k, v] = query.split('='); result[k] = decodeURI(v); return result; }, {});
|
||||||
if (this.session.app.callbackUrl) {
|
if (this.session.app.callbackUrl) {
|
||||||
|
const url = new URL(this.session.app.callbackUrl);
|
||||||
|
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url');
|
||||||
location.href = `${this.session.app.callbackUrl}?token=${this.session.token}&code=${this.session.token}&state=${getUrlParams().state || ''}`;
|
location.href = `${this.session.app.callbackUrl}?token=${this.session.token}&code=${this.session.token}&state=${getUrlParams().state || ''}`;
|
||||||
}
|
}
|
||||||
}, onLogin(res) {
|
}, onLogin(res) {
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<swiper-slide>
|
<swiper-slide>
|
||||||
<div class="_formRoot">
|
<div class="_formRoot">
|
||||||
<div class="fnfelxur">
|
<div class="fnfelxur">
|
||||||
<img :src="instance.iconUrl || instance.faviconUrl" alt="" class="icon"/>
|
<img :src="faviconUrl" alt="" class="icon"/>
|
||||||
<span class="name">{{ instance.name || `(${i18n.ts.unknown})` }}</span>
|
<span class="name">{{ instance.name || `(${i18n.ts.unknown})` }}</span>
|
||||||
</div>
|
</div>
|
||||||
<MkKeyValue :copy="host" oneline style="margin: 1em 0;">
|
<MkKeyValue :copy="host" oneline style="margin: 1em 0;">
|
||||||
|
@ -156,6 +156,7 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
import 'swiper/scss';
|
import 'swiper/scss';
|
||||||
import 'swiper/scss/virtual';
|
import 'swiper/scss/virtual';
|
||||||
|
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
host: string;
|
host: string;
|
||||||
|
@ -171,6 +172,7 @@ let meta = $ref<misskey.entities.DetailedInstanceMetadata | null>(null);
|
||||||
let instance = $ref<misskey.entities.Instance | null>(null);
|
let instance = $ref<misskey.entities.Instance | null>(null);
|
||||||
let suspended = $ref(false);
|
let suspended = $ref(false);
|
||||||
let isBlocked = $ref(false);
|
let isBlocked = $ref(false);
|
||||||
|
let faviconUrl = $ref(null);
|
||||||
|
|
||||||
const usersPagination = {
|
const usersPagination = {
|
||||||
endpoint: iAmModerator ? 'admin/show-users' : 'users' as const,
|
endpoint: iAmModerator ? 'admin/show-users' : 'users' as const,
|
||||||
|
@ -189,6 +191,7 @@ async function fetch() {
|
||||||
});
|
});
|
||||||
suspended = instance.isSuspended;
|
suspended = instance.isSuspended;
|
||||||
isBlocked = instance.isBlocked;
|
isBlocked = instance.isBlocked;
|
||||||
|
faviconUrl = getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.iconUrl, 'preview');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleBlock(ev) {
|
async function toggleBlock(ev) {
|
||||||
|
|
|
@ -70,6 +70,8 @@ async function accept(): Promise<void> {
|
||||||
|
|
||||||
state = 'accepted';
|
state = 'accepted';
|
||||||
if (props.callback) {
|
if (props.callback) {
|
||||||
|
const cbUrl = new URL(props.callback);
|
||||||
|
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(cbUrl.protocol)) throw new Error('invalid url');
|
||||||
location.href = appendQuery(props.callback, query({
|
location.href = appendQuery(props.callback, query({
|
||||||
session: props.session,
|
session: props.session,
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -24,7 +24,11 @@ export function createAiScriptEnv(opts) {
|
||||||
return confirm.canceled ? values.FALSE : values.TRUE;
|
return confirm.canceled ? values.FALSE : values.TRUE;
|
||||||
}),
|
}),
|
||||||
"Mk:api": values.FN_NATIVE(async ([ep, param, token]) => {
|
"Mk:api": values.FN_NATIVE(async ([ep, param, token]) => {
|
||||||
if (token) utils.assertString(token);
|
if (token) {
|
||||||
|
utils.assertString(token);
|
||||||
|
// バグがあればundefinedもあり得るため念のため
|
||||||
|
if (typeof token.value !== "string") throw new Error("invalid token");
|
||||||
|
}
|
||||||
apiRequests++;
|
apiRequests++;
|
||||||
if (apiRequests > 16) return values.NULL;
|
if (apiRequests > 16) return values.NULL;
|
||||||
const res = await os.api(
|
const res = await os.api(
|
||||||
|
|
|
@ -125,19 +125,22 @@ export function getUserMenu(user, router: Router = mainRouter) {
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await os.apiWithDialog(user.isBlocking ? "blocking/delete" : "blocking/create", {
|
await os.apiWithDialog(
|
||||||
|
user.isBlocking ? "blocking/delete" : "blocking/create",
|
||||||
|
{
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
})
|
},
|
||||||
|
);
|
||||||
user.isBlocking = !user.isBlocking;
|
user.isBlocking = !user.isBlocking;
|
||||||
await os.api(user.isBlocking ? "mute/create" : "mute/delete", {
|
await os.api(user.isBlocking ? "mute/create" : "mute/delete", {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
})
|
});
|
||||||
user.isMuted = user.isBlocking;
|
user.isMuted = user.isBlocking;
|
||||||
if (user.isBlocking) {
|
if (user.isBlocking) {
|
||||||
await os.api('following/delete', {
|
await os.api("following/delete", {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
user.isFollowing = false
|
user.isFollowing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<transition name="change" mode="default">
|
<transition name="change" mode="default">
|
||||||
<MarqueeText :key="key" :duration="marqueeDuration" :reverse="marqueeReverse">
|
<MarqueeText :key="key" :duration="marqueeDuration" :reverse="marqueeReverse">
|
||||||
<span v-for="instance in instances" :key="instance.id" class="item" :class="{ colored }" :style="{ background: colored ? instance.themeColor : null }">
|
<span v-for="instance in instances" :key="instance.id" class="item" :class="{ colored }" :style="{ background: colored ? instance.themeColor : null }">
|
||||||
<img v-if="instance.iconUrl" class="icon" :src="instance.iconUrl" alt=""/>
|
<img class="icon" :src="getInstanceIcon(instance)" alt=""/>
|
||||||
<MkA :to="`/instance-info/${instance.host}`" class="host _monospace">
|
<MkA :to="`/instance-info/${instance.host}`" class="host _monospace">
|
||||||
{{ instance.host }}
|
{{ instance.host }}
|
||||||
</MkA>
|
</MkA>
|
||||||
|
@ -27,6 +27,7 @@ import * as os from '@/os';
|
||||||
import { useInterval } from '@/scripts/use-interval';
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
import { getNoteSummary } from '@/scripts/get-note-summary';
|
import { getNoteSummary } from '@/scripts/get-note-summary';
|
||||||
import { notePage } from '@/filters/note';
|
import { notePage } from '@/filters/note';
|
||||||
|
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
display?: 'marquee' | 'oneByOne';
|
display?: 'marquee' | 'oneByOne';
|
||||||
|
@ -56,6 +57,10 @@ useInterval(tick, Math.max(5000, props.refreshIntervalSec * 1000), {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
afterMounted: true,
|
afterMounted: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getInstanceIcon(instance): string {
|
||||||
|
return getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<MkLoading v-if="fetching"/>
|
<MkLoading v-if="fetching"/>
|
||||||
<transition-group v-else tag="div" :name="$store.state.animation ? 'chart' : ''" class="instances">
|
<transition-group v-else tag="div" :name="$store.state.animation ? 'chart' : ''" class="instances">
|
||||||
<div v-for="(instance, i) in instances" :key="instance.id" class="instance">
|
<div v-for="(instance, i) in instances" :key="instance.id" class="instance">
|
||||||
<img v-if="instance.iconUrl" :src="instance.iconUrl" alt=""/>
|
<img :src="getInstanceIcon(instance)" alt=""/>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<a class="a" :href="'https://' + instance.host" target="_blank" :title="instance.host">{{ instance.host }}</a>
|
<a class="a" :href="'https://' + instance.host" target="_blank" :title="instance.host">{{ instance.host }}</a>
|
||||||
<p>{{ instance.softwareName || '?' }} {{ instance.softwareVersion }}</p>
|
<p>{{ instance.softwareName || '?' }} {{ instance.softwareVersion }}</p>
|
||||||
|
@ -27,6 +27,7 @@ import MkMiniChart from '@/components/MkMiniChart.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { useInterval } from '@/scripts/use-interval';
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
|
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
|
||||||
|
|
||||||
const name = 'federation';
|
const name = 'federation';
|
||||||
|
|
||||||
|
@ -71,6 +72,10 @@ useInterval(fetch, 1000 * 60, {
|
||||||
afterMounted: true,
|
afterMounted: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getInstanceIcon(instance): string {
|
||||||
|
return getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png';
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose<WidgetComponentExpose>({
|
defineExpose<WidgetComponentExpose>({
|
||||||
name,
|
name,
|
||||||
configure,
|
configure,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<MkTagCloud v-if="activeInstances">
|
<MkTagCloud v-if="activeInstances">
|
||||||
<li v-for="instance in activeInstances" :key="instance.id">
|
<li v-for="instance in activeInstances" :key="instance.id">
|
||||||
<a @click.prevent="onInstanceClick(instance)">
|
<a @click.prevent="onInstanceClick(instance)">
|
||||||
<img style="width: 32px;" :src="instance.iconUrl">
|
<img style="width: 32px;" :src="getInstanceIcon(instance)">
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</MkTagCloud>
|
</MkTagCloud>
|
||||||
|
@ -21,6 +21,7 @@ import MkContainer from '@/components/MkContainer.vue';
|
||||||
import MkTagCloud from '@/components/MkTagCloud.vue';
|
import MkTagCloud from '@/components/MkTagCloud.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { useInterval } from '@/scripts/use-interval';
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
|
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
|
||||||
|
|
||||||
const name = 'instanceCloud';
|
const name = 'instanceCloud';
|
||||||
|
|
||||||
|
@ -65,6 +66,10 @@ useInterval(() => {
|
||||||
afterMounted: true,
|
afterMounted: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getInstanceIcon(instance): string {
|
||||||
|
return getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png';
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose<WidgetComponentExpose>({
|
defineExpose<WidgetComponentExpose>({
|
||||||
name,
|
name,
|
||||||
configure,
|
configure,
|
||||||
|
|
|
@ -3335,7 +3335,7 @@ packages:
|
||||||
/axios/0.24.0:
|
/axios/0.24.0:
|
||||||
resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==}
|
resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects: 1.15.2
|
follow-redirects: 1.15.2_debug@4.3.4
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- debug
|
- debug
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -3343,7 +3343,7 @@ packages:
|
||||||
/axios/0.25.0_debug@4.3.4:
|
/axios/0.25.0_debug@4.3.4:
|
||||||
resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==}
|
resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects: 1.15.2
|
follow-redirects: 1.15.2_debug@4.3.4
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- debug
|
- debug
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -3351,7 +3351,7 @@ packages:
|
||||||
/axios/1.2.2:
|
/axios/1.2.2:
|
||||||
resolution: {integrity: sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==}
|
resolution: {integrity: sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects: 1.15.2
|
follow-redirects: 1.15.2_debug@4.3.4
|
||||||
form-data: 4.0.0
|
form-data: 4.0.0
|
||||||
proxy-from-env: 1.1.0
|
proxy-from-env: 1.1.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@ -3361,7 +3361,7 @@ packages:
|
||||||
/axios/1.3.2:
|
/axios/1.3.2:
|
||||||
resolution: {integrity: sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==}
|
resolution: {integrity: sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects: 1.15.2
|
follow-redirects: 1.15.2_debug@4.3.4
|
||||||
form-data: 4.0.0
|
form-data: 4.0.0
|
||||||
proxy-from-env: 1.1.0
|
proxy-from-env: 1.1.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@ -6301,7 +6301,7 @@ packages:
|
||||||
readable-stream: 2.3.7
|
readable-stream: 2.3.7
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/follow-redirects/1.15.2:
|
/follow-redirects/1.15.2_debug@4.3.4:
|
||||||
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
|
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
|
||||||
engines: {node: '>=4.0'}
|
engines: {node: '>=4.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -6309,6 +6309,8 @@ packages:
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
debug:
|
debug:
|
||||||
optional: true
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
debug: 4.3.4
|
||||||
|
|
||||||
/for-each/0.3.3:
|
/for-each/0.3.3:
|
||||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||||
|
|
Loading…
Reference in New Issue