-
+
+
Recent Videos
Go to Youtube
-
+
-
+
diff --git a/packages/nocodb/src/controllers/utils.controller.ts b/packages/nocodb/src/controllers/utils.controller.ts
index 05fb63ec71..36ff243553 100644
--- a/packages/nocodb/src/controllers/utils.controller.ts
+++ b/packages/nocodb/src/controllers/utils.controller.ts
@@ -7,6 +7,7 @@ import {
HttpCode,
Post,
Req,
+ Request,
UseGuards,
} from '@nestjs/common';
import { ProjectRoles, validateAndExtractSSLProp } from 'nocodb-sdk';
@@ -166,4 +167,16 @@ export class UtilsController {
// todo: refactor
return (await this.utilsService.aggregatedMetaInfo()) as any;
}
+
+ @UseGuards(PublicApiLimiterGuard)
+ @Get('/api/v1/feed')
+ async feed(@Request() req: NcRequest) {
+ return await this.utilsService.feed(req);
+ }
+
+ @UseGuards(PublicApiLimiterGuard)
+ @Get('/api/v1/new-feed')
+ async newFeed(@Request() req: NcRequest) {
+ return await this.utilsService.getLatestFeed(req);
+ }
}
diff --git a/packages/nocodb/src/services/utils.service.ts b/packages/nocodb/src/services/utils.service.ts
index 76785ac8c3..4c9b826f5e 100644
--- a/packages/nocodb/src/services/utils.service.ts
+++ b/packages/nocodb/src/services/utils.service.ts
@@ -5,7 +5,7 @@ import { compareVersions, validate } from 'compare-versions';
import { ViewTypes } from 'nocodb-sdk';
import { ConfigService } from '@nestjs/config';
import { useAgent } from 'request-filtering-agent';
-import type { AppConfig } from '~/interface/config';
+import type { AppConfig, NcRequest } from '~/interface/config';
import { NC_APP_SETTINGS, NC_ATTACHMENT_FIELD_SIZE } from '~/constants';
import SqlMgrv2 from '~/db/sql-mgr/v2/SqlMgrv2';
import { NcError } from '~/helpers/catchError';
@@ -13,7 +13,7 @@ import { Base, Store, User } from '~/models';
import Noco from '~/Noco';
import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2';
import getInstance from '~/utils/getInstance';
-import { MetaTable, RootScopes } from '~/utils/globals';
+import { CacheScope, MetaTable, RootScopes } from '~/utils/globals';
import { jdbcToXcConfig } from '~/utils/nc-config/helpers';
import { packageVersion } from '~/utils/packageVersion';
import {
@@ -21,6 +21,8 @@ import {
defaultLimitConfig,
} from '~/helpers/extractLimitAndOffset';
import { DriverClient } from '~/utils/nc-config';
+import NocoCache from '~/cache/NocoCache';
+import { getCircularReplacer } from '~/utils';
const versionCache = {
releaseVersion: null,
@@ -385,7 +387,6 @@ export class UtilsService {
if (result.status === 'fulfilled') {
return result.value;
}
- console.log(result.reason);
return null;
});
};
@@ -460,6 +461,7 @@ export class UtilsService {
disableEmailAuth: this.configService.get('auth.disableEmailAuth', {
infer: true,
}),
+ feedEnabled: process.env.NC_DISABLE_PRODUCT_FEED !== 'true',
mainSubDomain: this.configService.get('mainSubDomain', { infer: true }),
dashboardPath: this.configService.get('dashboardPath', { infer: true }),
inviteOnlySignup: settings.invite_only_signup,
@@ -471,4 +473,99 @@ export class UtilsService {
return result;
}
+
+ async feed(req: NcRequest) {
+ const {
+ type = 'all',
+ page = '1',
+ per_page = '10',
+ } = req.query as {
+ type: 'github' | 'youtube' | 'all' | 'twitter' | 'cloud';
+ page: string;
+ per_page: string;
+ };
+
+ const cacheKey = `${CacheScope.PRODUCT_FEED}:${type}:${page}:${per_page}`;
+
+ const cachedData = await NocoCache.get(cacheKey, 'json');
+
+ if (cachedData) {
+ try {
+ return JSON.parse(cachedData);
+ } catch (e) {
+ console.log(e);
+ }
+ }
+
+ let response;
+
+ try {
+ response = await axios.get('https://nocodb.com/api/v1/social/feed', {
+ params: {
+ per_page,
+ page,
+ type,
+ },
+ });
+ } catch (e) {
+ console.log(e);
+ return [];
+ }
+
+ // The feed includes the attachments, which has the presigned URL
+ // So the cache should match the presigned URL cache
+ await NocoCache.setExpiring(
+ cacheKey,
+ JSON.stringify(response.data, getCircularReplacer),
+ isNaN(parseInt(process.env.NC_ATTACHMENT_EXPIRE_SECONDS))
+ ? 2 * 60 * 60
+ : parseInt(process.env.NC_ATTACHMENT_EXPIRE_SECONDS),
+ );
+
+ return response.data;
+ }
+
+ async getLatestFeed(req: NcRequest) {
+ const { last_published_at } = req.query as {
+ last_published_at: string;
+ };
+
+ if (!last_published_at) {
+ return 0;
+ }
+
+ const utils = {
+ found: false,
+ page: 1,
+ missedItems: 0,
+ };
+ while (!utils.found) {
+ const feed = await this.feed({
+ query: {
+ type: 'all',
+ page: utils.page.toString(),
+ per_page: '100',
+ },
+ } as unknown as NcRequest);
+
+ if (!feed || !feed?.length) {
+ break;
+ }
+
+ for (const item of feed) {
+ if (item['Published Time'] === last_published_at) {
+ utils.found = true;
+ break;
+ }
+
+ utils.missedItems++;
+ }
+
+ if (!utils.found) {
+ utils.page++;
+ }
+ }
+
+ return utils.missedItems;
+ }
}
diff --git a/packages/nocodb/src/utils/globals.ts b/packages/nocodb/src/utils/globals.ts
index c0d0f1e1bb..d6e2b2e960 100644
--- a/packages/nocodb/src/utils/globals.ts
+++ b/packages/nocodb/src/utils/globals.ts
@@ -187,6 +187,7 @@ export enum CacheScope {
INTEGRATION = 'integration',
COL_BUTTON = 'colButton',
CMD_PALETTE = 'cmdPalette',
+ PRODUCT_FEED = 'productFeed',
}
export enum CacheGetType {