-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add ci * add github yml * update ci * update ci * update error ci * update * resolver issue #6 * ci * udpate ci * update ci * a * add github integration and update mknote * update
- Loading branch information
Showing
11 changed files
with
488 additions
and
58 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,295 @@ | ||
import type Koa from "koa"; | ||
import Router from "@koa/router"; | ||
import { OAuth2 } from "oauth"; | ||
import { v4 as uuid } from "uuid"; | ||
import { IsNull } from "typeorm"; | ||
import { getJson } from "@/misc/fetch.js"; | ||
import config from "@/config/index.js"; | ||
import { publishMainStream } from "@/services/stream.js"; | ||
import { fetchMeta } from "@/misc/fetch-meta.js"; | ||
import { Users, UserProfiles } from "@/models/index.js"; | ||
import type { ILocalUser } from "@/models/entities/user.js"; | ||
import { redisClient } from "../../../db/redis.js"; | ||
import signin from "../common/signin.js"; | ||
|
||
function getUserToken(ctx: Koa.BaseContext): string | null { | ||
return ((ctx.headers["cookie"] || "").match(/igi=(\w+)/) || [null, null])[1]; | ||
} | ||
|
||
function compareOrigin(ctx: Koa.BaseContext): boolean { | ||
function normalizeUrl(url?: string): string { | ||
return url ? (url.endsWith("/") ? url.slice(0, url.length - 1) : url) : ""; | ||
} | ||
|
||
const referer = ctx.headers["referer"]; | ||
|
||
return normalizeUrl(referer) === normalizeUrl(config.url); | ||
} | ||
|
||
// Init router | ||
const router = new Router(); | ||
|
||
router.get("/disconnect/github", async (ctx) => { | ||
if (!compareOrigin(ctx)) { | ||
ctx.throw(400, "invalid origin"); | ||
return; | ||
} | ||
|
||
const userToken = getUserToken(ctx); | ||
if (!userToken) { | ||
ctx.throw(400, "signin required"); | ||
return; | ||
} | ||
|
||
const user = await Users.findOneByOrFail({ | ||
host: IsNull(), | ||
token: userToken, | ||
}); | ||
|
||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); | ||
|
||
profile.integrations.github = undefined; | ||
|
||
await UserProfiles.update(user.id, { | ||
integrations: profile.integrations, | ||
}); | ||
|
||
ctx.body = "El enlace de GitHub ha sido cancelado. :v:"; | ||
|
||
// Publish i updated event | ||
publishMainStream( | ||
user.id, | ||
"meUpdated", | ||
await Users.pack(user, user, { | ||
detail: true, | ||
includeSecrets: true, | ||
}), | ||
); | ||
}); | ||
|
||
async function getOath2() { | ||
const meta = await fetchMeta(true); | ||
|
||
if ( | ||
meta.enableGithubIntegration && | ||
meta.githubClientId && | ||
meta.githubClientSecret | ||
) { | ||
return new OAuth2( | ||
meta.githubClientId, | ||
meta.githubClientSecret, | ||
"https://github.com/", | ||
"login/oauth/authorize", | ||
"login/oauth/access_token", | ||
); | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
router.get("/connect/github", async (ctx) => { | ||
if (!compareOrigin(ctx)) { | ||
ctx.throw(400, "invalid origin"); | ||
return; | ||
} | ||
|
||
const userToken = getUserToken(ctx); | ||
if (!userToken) { | ||
ctx.throw(400, "signin required"); | ||
return; | ||
} | ||
|
||
const params = { | ||
redirect_uri: `${config.url}/api/gh/cb`, | ||
scope: ["read:user"], | ||
state: uuid(), | ||
}; | ||
|
||
redisClient.set(userToken, JSON.stringify(params)); | ||
|
||
const oauth2 = await getOath2(); | ||
ctx.redirect(oauth2!.getAuthorizeUrl(params)); | ||
}); | ||
|
||
router.get("/signin/github", async (ctx) => { | ||
const sessid = uuid(); | ||
|
||
const params = { | ||
redirect_uri: `${config.url}/api/gh/cb`, | ||
scope: ["read:user"], | ||
state: uuid(), | ||
}; | ||
|
||
ctx.cookies.set("signin_with_github_sid", sessid, { | ||
path: "/", | ||
secure: config.url.startsWith("https"), | ||
httpOnly: true, | ||
}); | ||
|
||
redisClient.set(sessid, JSON.stringify(params)); | ||
|
||
const oauth2 = await getOath2(); | ||
ctx.redirect(oauth2!.getAuthorizeUrl(params)); | ||
}); | ||
|
||
router.get("/gh/cb", async (ctx) => { | ||
const userToken = getUserToken(ctx); | ||
|
||
const oauth2 = await getOath2(); | ||
|
||
if (!userToken) { | ||
const sessid = ctx.cookies.get("signin_with_github_sid"); | ||
|
||
if (!sessid) { | ||
ctx.throw(400, "invalid session"); | ||
return; | ||
} | ||
|
||
const code = ctx.query.code; | ||
|
||
if (!code || typeof code !== "string") { | ||
ctx.throw(400, "invalid session"); | ||
return; | ||
} | ||
|
||
const { redirect_uri, state } = await new Promise<any>((res, rej) => { | ||
redisClient.get(sessid, async (_, state) => { | ||
res(JSON.parse(state)); | ||
}); | ||
}); | ||
|
||
if (ctx.query.state !== state) { | ||
ctx.throw(400, "invalid session"); | ||
return; | ||
} | ||
|
||
const { accessToken } = await new Promise<any>((res, rej) => | ||
oauth2!.getOAuthAccessToken( | ||
code, | ||
{ | ||
redirect_uri, | ||
}, | ||
(err, accessToken, refresh, result) => { | ||
if (err) { | ||
rej(err); | ||
} else if (result.error) { | ||
rej(result.error); | ||
} else { | ||
res({ accessToken }); | ||
} | ||
}, | ||
), | ||
); | ||
|
||
const { login, id } = (await getJson( | ||
"https://api.github.com/user", | ||
"application/vnd.github.v3+json", | ||
10 * 1000, | ||
{ | ||
Authorization: `bearer ${accessToken}`, | ||
}, | ||
)) as Record<string, unknown>; | ||
if (typeof login !== "string" || typeof id !== "string") { | ||
ctx.throw(400, "invalid session"); | ||
return; | ||
} | ||
|
||
const link = await UserProfiles.createQueryBuilder() | ||
.where("\"integrations\"->'github'->>'id' = :id", { id: id }) | ||
.andWhere('"userHost" IS NULL') | ||
.getOne(); | ||
|
||
if (link == null) { | ||
ctx.throw( | ||
404, | ||
`@${login}No había ninguna cuenta de Fedired vinculada con...`, | ||
); | ||
return; | ||
} | ||
|
||
signin( | ||
ctx, | ||
(await Users.findOneBy({ id: link.userId })) as ILocalUser, | ||
true, | ||
); | ||
} else { | ||
const code = ctx.query.code; | ||
|
||
if (!code || typeof code !== "string") { | ||
ctx.throw(400, "invalid session"); | ||
return; | ||
} | ||
|
||
const { redirect_uri, state } = await new Promise<any>((res, rej) => { | ||
redisClient.get(userToken, async (_, state) => { | ||
res(JSON.parse(state)); | ||
}); | ||
}); | ||
|
||
if (ctx.query.state !== state) { | ||
ctx.throw(400, "invalid session"); | ||
return; | ||
} | ||
|
||
const { accessToken } = await new Promise<any>((res, rej) => | ||
oauth2!.getOAuthAccessToken( | ||
code, | ||
{ redirect_uri }, | ||
(err, accessToken, refresh, result) => { | ||
if (err) { | ||
rej(err); | ||
} else if (result.error) { | ||
rej(result.error); | ||
} else { | ||
res({ accessToken }); | ||
} | ||
}, | ||
), | ||
); | ||
|
||
const { login, id } = (await getJson( | ||
"https://api.github.com/user", | ||
"application/vnd.github.v3+json", | ||
10 * 1000, | ||
{ | ||
Authorization: `bearer ${accessToken}`, | ||
}, | ||
)) as Record<string, unknown>; | ||
|
||
if (typeof login !== "string" || typeof id !== "string") { | ||
ctx.throw(400, "invalid session"); | ||
return; | ||
} | ||
|
||
const user = await Users.findOneByOrFail({ | ||
host: IsNull(), | ||
token: userToken, | ||
}); | ||
|
||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); | ||
|
||
await UserProfiles.update(user.id, { | ||
integrations: { | ||
...profile.integrations, | ||
github: { | ||
accessToken: accessToken, | ||
id: id, | ||
login: login, | ||
}, | ||
}, | ||
}); | ||
|
||
ctx.body = `GitHub: @${login} conectado a Fedired: @${user.username}! `; | ||
// Publish i updated event | ||
publishMainStream( | ||
user.id, | ||
"meUpdated", | ||
await Users.pack(user, user, { | ||
detail: true, | ||
includeSecrets: true, | ||
}), | ||
); | ||
} | ||
}); | ||
|
||
export default router; |
Oops, something went wrong.