mirror of
https://github.com/wassname/talk.git
synced 2026-07-03 20:23:29 +08:00
downloads by user id (#2500)
This commit is contained in:
committed by
Kim Gardner
parent
ffecd5e981
commit
0e37a474fa
@@ -27,6 +27,8 @@ export type DownloadOptions = Pick<
|
||||
"mongo" | "redis" | "signingConfig" | "config"
|
||||
>;
|
||||
|
||||
export type AdminDownloadOptions = Pick<AppOptions, "mongo" | "signingConfig">;
|
||||
|
||||
async function sendExport(
|
||||
mongo: Db,
|
||||
tenant: Tenant,
|
||||
@@ -216,6 +218,46 @@ export const downloadHandler = ({
|
||||
};
|
||||
};
|
||||
|
||||
export const adminDownloadHandler = ({
|
||||
mongo,
|
||||
signingConfig,
|
||||
}: AdminDownloadOptions): RequestHandler => {
|
||||
return async (req: Request, res, next) => {
|
||||
// Tenant is guaranteed at this point.
|
||||
const coral = req.coral!;
|
||||
const tenant = coral.tenant!;
|
||||
const { token } = req.query;
|
||||
|
||||
const { sub: userID } = decodeJWT(token);
|
||||
if (!userID) {
|
||||
return res.sendStatus(400);
|
||||
}
|
||||
|
||||
try {
|
||||
const {
|
||||
token: { iat },
|
||||
user,
|
||||
} = await verifyDownloadTokenString(
|
||||
mongo,
|
||||
tenant,
|
||||
signingConfig,
|
||||
token,
|
||||
coral.now
|
||||
);
|
||||
|
||||
// Only load comments since this download token was issued.
|
||||
const latestContentDate = new Date(iat * 1000);
|
||||
|
||||
// Send the export down the response.
|
||||
await sendExport(mongo, tenant, user, latestContentDate, res);
|
||||
|
||||
return;
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export type DownloadCheckOptions = Pick<
|
||||
AppOptions,
|
||||
"mongo" | "redis" | "signingConfig" | "config"
|
||||
|
||||
@@ -3,6 +3,7 @@ import express from "express";
|
||||
|
||||
import { AppOptions } from "coral-server/app";
|
||||
import {
|
||||
adminDownloadHandler,
|
||||
confirmCheckHandler,
|
||||
confirmHandler,
|
||||
confirmRequestHandler,
|
||||
@@ -34,6 +35,7 @@ export function createNewAccountRouter(
|
||||
router.put("/invite", jsonMiddleware, inviteHandler(app));
|
||||
|
||||
router.get("/downloadcheck", downloadCheckHandler(app));
|
||||
router.get("/download", adminDownloadHandler(app));
|
||||
router.post(
|
||||
"/download",
|
||||
bodyParser.urlencoded({
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
removeIgnore,
|
||||
removeSuspension,
|
||||
requestCommentsDownload,
|
||||
requestUserCommentsDownload,
|
||||
setEmail,
|
||||
setPassword,
|
||||
setUsername,
|
||||
@@ -35,6 +36,7 @@ import {
|
||||
GQLRemoveUserIgnoreInput,
|
||||
GQLRemoveUserSuspensionInput,
|
||||
GQLRequestCommentsDownloadInput,
|
||||
GQLRequestUserCommentsDownloadInput,
|
||||
GQLSetEmailInput,
|
||||
GQLSetPasswordInput,
|
||||
GQLSetUsernameInput,
|
||||
@@ -187,6 +189,17 @@ export const Users = (ctx: TenantContext) => ({
|
||||
ignore(ctx.mongo, ctx.tenant, ctx.user!, input.userID, ctx.now),
|
||||
removeIgnore: async (input: GQLRemoveUserIgnoreInput) =>
|
||||
removeIgnore(ctx.mongo, ctx.tenant, ctx.user!, input.userID),
|
||||
requestUserCommentsDownload: async (
|
||||
input: GQLRequestUserCommentsDownloadInput
|
||||
) =>
|
||||
requestUserCommentsDownload(
|
||||
ctx.mongo,
|
||||
ctx.tenant,
|
||||
ctx.config,
|
||||
ctx.signingConfig!,
|
||||
input.userID,
|
||||
ctx.now
|
||||
),
|
||||
requestCommentsDownload: async (input: GQLRequestCommentsDownloadInput) =>
|
||||
requestCommentsDownload(
|
||||
ctx.mongo,
|
||||
|
||||
@@ -189,4 +189,8 @@ export const Mutation: Required<GQLMutationTypeResolver<void>> = {
|
||||
user: await ctx.mutators.Users.requestCommentsDownload(input),
|
||||
clientMutationId: input.clientMutationId,
|
||||
}),
|
||||
requestUserCommentsDownload: async (sourc, { input }, ctx) => ({
|
||||
archiveURL: await ctx.mutators.Users.requestUserCommentsDownload(input),
|
||||
clientMutationId: input.clientMutationId,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -4579,6 +4579,33 @@ type RequestCommentsDownloadPayload {
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
#########################
|
||||
# requestUserCommentsDownload
|
||||
#########################
|
||||
|
||||
input RequestUserCommentsDownloadInput {
|
||||
"""
|
||||
userID specifies user to download comments for
|
||||
"""
|
||||
userID: String!
|
||||
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
}
|
||||
|
||||
type RequestUserCommentsDownloadPayload {
|
||||
"""
|
||||
clientMutationId is required for Relay support.
|
||||
"""
|
||||
clientMutationId: String!
|
||||
|
||||
"""
|
||||
archiveURL is the archive url
|
||||
"""
|
||||
archiveURL: String!
|
||||
}
|
||||
##################
|
||||
## Mutation
|
||||
##################
|
||||
@@ -4862,6 +4889,13 @@ type Mutation {
|
||||
requestCommentsDownload(
|
||||
input: RequestCommentsDownloadInput!
|
||||
): RequestCommentsDownloadPayload! @auth(permit: [SUSPENDED, BANNED])
|
||||
|
||||
"""
|
||||
requestUserCommentsDownload allows a user to request to download their comments.
|
||||
"""
|
||||
requestUserCommentsDownload(
|
||||
input: RequestUserCommentsDownloadInput!
|
||||
): RequestUserCommentsDownloadPayload! @auth(roles: [ADMIN])
|
||||
}
|
||||
|
||||
##################
|
||||
|
||||
@@ -25,7 +25,7 @@ const DownloadTokenSchema = StandardClaimsSchema.keys({
|
||||
aud: Joi.string().only("download"),
|
||||
});
|
||||
|
||||
export async function generateDownloadLink(
|
||||
export async function generateDownloadToken(
|
||||
userID: string,
|
||||
tenant: Tenant,
|
||||
config: Config,
|
||||
@@ -46,7 +46,23 @@ export async function generateDownloadLink(
|
||||
aud: "download",
|
||||
};
|
||||
|
||||
const token = await signString(signingConfig, downloadToken);
|
||||
return await signString(signingConfig, downloadToken);
|
||||
}
|
||||
|
||||
export async function generateDownloadLink(
|
||||
userID: string,
|
||||
tenant: Tenant,
|
||||
config: Config,
|
||||
signingConfig: JWTSigningConfig,
|
||||
now: Date
|
||||
) {
|
||||
const token = await generateDownloadToken(
|
||||
userID,
|
||||
tenant,
|
||||
config,
|
||||
signingConfig,
|
||||
now
|
||||
);
|
||||
|
||||
return constructTenantURL(
|
||||
config,
|
||||
@@ -55,6 +71,28 @@ export async function generateDownloadLink(
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateAdminDownloadLink(
|
||||
userID: string,
|
||||
tenant: Tenant,
|
||||
config: Config,
|
||||
signingConfig: JWTSigningConfig,
|
||||
now: Date
|
||||
) {
|
||||
const token = await generateDownloadToken(
|
||||
userID,
|
||||
tenant,
|
||||
config,
|
||||
signingConfig,
|
||||
now
|
||||
);
|
||||
|
||||
return constructTenantURL(
|
||||
config,
|
||||
tenant,
|
||||
`/api/account/download?token=${token}`
|
||||
);
|
||||
}
|
||||
|
||||
export function validateDownloadToken(
|
||||
token: DownloadToken | object
|
||||
): Error | null {
|
||||
|
||||
@@ -65,7 +65,10 @@ import { sendConfirmationEmail } from "coral-server/services/users/auth";
|
||||
|
||||
import { JWTSigningConfig, signPATString } from "coral-server/services/jwt";
|
||||
|
||||
import { generateDownloadLink } from "./download/download";
|
||||
import {
|
||||
generateAdminDownloadLink,
|
||||
generateDownloadLink,
|
||||
} from "./download/download";
|
||||
import { validateEmail, validatePassword, validateUsername } from "./helpers";
|
||||
|
||||
export type InsertUser = InsertUserInput;
|
||||
@@ -917,3 +920,22 @@ export async function requestCommentsDownload(
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
export async function requestUserCommentsDownload(
|
||||
mongo: Db,
|
||||
tenant: Tenant,
|
||||
config: Config,
|
||||
signingConfig: JWTSigningConfig,
|
||||
userID: string,
|
||||
now: Date
|
||||
) {
|
||||
const downloadUrl = await generateAdminDownloadLink(
|
||||
userID,
|
||||
tenant,
|
||||
config,
|
||||
signingConfig,
|
||||
now
|
||||
);
|
||||
|
||||
return downloadUrl;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user