Add simple count of views to share links (#4036)
* Add simple count of views to share links * Remove no longer applicable tests * Avoid incrementing view count for known bots
This commit is contained in:
@@ -97,10 +97,6 @@ export default async function loadDocument({
|
||||
const canReadDocument = user && can(user, "read", document);
|
||||
|
||||
if (canReadDocument) {
|
||||
await share.update({
|
||||
lastAccessedAt: new Date(),
|
||||
});
|
||||
|
||||
// Cannot use document.collection here as it does not include the
|
||||
// documentStructure by default through the relationship.
|
||||
collection = await Collection.findByPk(document.collectionId);
|
||||
@@ -156,10 +152,6 @@ export default async function loadDocument({
|
||||
if (!team.sharing) {
|
||||
throw AuthorizationError();
|
||||
}
|
||||
|
||||
await share.update({
|
||||
lastAccessedAt: new Date(),
|
||||
});
|
||||
} else {
|
||||
document = await Document.findByPk(id as string, {
|
||||
userId: user ? user.id : undefined,
|
||||
|
||||
13
server/migrations/20220830215146-add-shares-views.js
Normal file
13
server/migrations/20220830215146-add-shares-views.js
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.addColumn("shares", "views", {
|
||||
type: Sequelize.INTEGER,
|
||||
defaultValue: 0
|
||||
});
|
||||
},
|
||||
down: async (queryInterface) => {
|
||||
await queryInterface.removeColumn("shares", "views");
|
||||
},
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Table,
|
||||
Scopes,
|
||||
DataType,
|
||||
Default,
|
||||
} from "sequelize-typescript";
|
||||
import Collection from "./Collection";
|
||||
import Document from "./Document";
|
||||
@@ -79,6 +80,11 @@ class Share extends IdModel {
|
||||
@Column
|
||||
lastAccessedAt: Date | null;
|
||||
|
||||
/** Total count of times the shared link has been accessed */
|
||||
@Default(0)
|
||||
@Column
|
||||
views: number;
|
||||
|
||||
// getters
|
||||
|
||||
get isRevoked() {
|
||||
|
||||
@@ -12,6 +12,7 @@ export default function present(share: Share, isAdmin = false) {
|
||||
createdBy: presentUser(share.user),
|
||||
includeChildDocuments: share.includeChildDocuments,
|
||||
lastAccessedAt: share.lastAccessedAt || undefined,
|
||||
views: share.views || 0,
|
||||
createdAt: share.createdAt,
|
||||
updatedAt: share.updatedAt,
|
||||
};
|
||||
|
||||
@@ -102,8 +102,6 @@ describe("#documents.info", () => {
|
||||
expect(body.data.id).toEqual(document.id);
|
||||
expect(body.data.createdBy).toEqual(undefined);
|
||||
expect(body.data.updatedBy).toEqual(undefined);
|
||||
await share.reload();
|
||||
expect(share.lastAccessedAt).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should not return document of a deleted collection, when the user was absent in the collection", async () => {
|
||||
@@ -192,8 +190,6 @@ describe("#documents.info", () => {
|
||||
expect(body.data.document.createdBy).toEqual(undefined);
|
||||
expect(body.data.document.updatedBy).toEqual(undefined);
|
||||
expect(body.data.sharedTree).toEqual(collection.documentStructure?.[0]);
|
||||
await share.reload();
|
||||
expect(share.lastAccessedAt).toBeTruthy();
|
||||
});
|
||||
it("should return sharedTree from shareId with id of nested document", async () => {
|
||||
const { document, user } = await seed();
|
||||
@@ -215,8 +211,6 @@ describe("#documents.info", () => {
|
||||
expect(body.data.document.createdBy).toEqual(undefined);
|
||||
expect(body.data.document.updatedBy).toEqual(undefined);
|
||||
expect(body.data.sharedTree).toEqual(document.toJSON());
|
||||
await share.reload();
|
||||
expect(share.lastAccessedAt).toBeTruthy();
|
||||
});
|
||||
it("should not return sharedTree if child documents not shared", async () => {
|
||||
const { document, user } = await seed();
|
||||
@@ -238,8 +232,6 @@ describe("#documents.info", () => {
|
||||
expect(body.data.document.createdBy).toEqual(undefined);
|
||||
expect(body.data.document.updatedBy).toEqual(undefined);
|
||||
expect(body.data.sharedTree).toEqual(undefined);
|
||||
await share.reload();
|
||||
expect(share.lastAccessedAt).toBeTruthy();
|
||||
});
|
||||
it("should not return details for nested documents", async () => {
|
||||
const { document, collection, user } = await seed();
|
||||
|
||||
@@ -3,6 +3,7 @@ import path from "path";
|
||||
import util from "util";
|
||||
import { Context, Next } from "koa";
|
||||
import { escape } from "lodash";
|
||||
import { Sequelize } from "sequelize";
|
||||
import documentLoader from "@server/commands/documentLoader";
|
||||
import env from "@server/env";
|
||||
import presentEnv from "@server/presenters/env";
|
||||
@@ -81,6 +82,13 @@ export const renderShare = async (ctx: Context, next: Next) => {
|
||||
});
|
||||
share = result.share;
|
||||
document = result.document;
|
||||
|
||||
if (share && !ctx.userAgent.isBot) {
|
||||
await share.update({
|
||||
lastAccessedAt: new Date(),
|
||||
views: Sequelize.literal("views + 1"),
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
// If the share or document does not exist, return a 404.
|
||||
ctx.status = 404;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import path from "path";
|
||||
import Koa from "koa";
|
||||
import Koa, { BaseContext } from "koa";
|
||||
import Router from "koa-router";
|
||||
import send from "koa-send";
|
||||
import serve from "koa-static";
|
||||
import userAgent, { UserAgentContext } from "koa-useragent";
|
||||
import { languages } from "@shared/i18n";
|
||||
import env from "@server/env";
|
||||
import { NotFoundError } from "@server/errors";
|
||||
@@ -22,6 +23,8 @@ koa.use(
|
||||
})
|
||||
);
|
||||
|
||||
koa.use<BaseContext, UserAgentContext>(userAgent);
|
||||
|
||||
if (isProduction) {
|
||||
router.get("/static/*", async (ctx) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user