feat: added crons

This commit is contained in:
Nevo David 2024-01-29 15:39:21 +07:00
parent fa327ebf9b
commit 48eaa9b7d5
20 changed files with 419 additions and 26 deletions

View File

@ -1,8 +1,3 @@
/**
* This is not a production server yet!
* This is only a minimal backend to get started.
*/
import {Logger, ValidationPipe} from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

View File

@ -41,7 +41,7 @@ export class AuthService {
}
private async loginOrRegisterProvider(provider: Provider, body: LoginUserDto) {
const providerInstance = ProvidersFactory.loadProviders(provider);
const providerInstance = ProvidersFactory.loadProvider(provider);
const providerUser = await providerInstance.getUser(body.providerToken);
if (!providerUser) {
throw new Error('Invalid provider token');

View File

@ -3,7 +3,7 @@ import {GithubProvider} from "@gitroom/backend/services/auth/providers/github.pr
import {ProvidersInterface} from "@gitroom/backend/services/auth/providers.interface";
export class ProvidersFactory {
static loadProviders(provider: Provider): ProvidersInterface {
static loadProvider(provider: Provider): ProvidersInterface {
switch (provider) {
case Provider.GITHUB:
return new GithubProvider();

18
apps/cron/.eslintrc.json Normal file
View File

@ -0,0 +1,18 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

11
apps/cron/jest.config.ts Normal file
View File

@ -0,0 +1,11 @@
/* eslint-disable */
export default {
displayName: 'consumers',
preset: '../../jest.preset.js',
testEnvironment: 'node',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/apps/consumers',
};

62
apps/cron/project.json Normal file
View File

@ -0,0 +1,62 @@
{
"name": "cron",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/cron/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/webpack:webpack",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"target": "node",
"compiler": "tsc",
"outputPath": "dist/apps/cron",
"main": "apps/cron/src/main.ts",
"tsConfig": "apps/cron/tsconfig.app.json",
"webpackConfig": "apps/cron/webpack.config.js"
},
"configurations": {
"development": {},
"production": {}
}
},
"serve": {
"executor": "@nx/js:node",
"defaultConfiguration": "development",
"options": {
"buildTarget": "cron:build"
},
"configurations": {
"development": {
"buildTarget": "cron:build:development"
},
"production": {
"buildTarget": "cron:build:production"
}
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/cron/**/*.ts"]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/cron/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
}
},
"tags": []
}

View File

@ -0,0 +1,11 @@
import { Injectable } from '@nestjs/common';
import {Interval} from '@nestjs/schedule';
import {isDev} from "@gitroom/helpers/utils/is.dev";
@Injectable()
export class CheckTrending {
@Interval(isDev() ? 10000 : 3600000)
CheckTrending() {
console.log('hello');
}
}

View File

@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule';
import {CheckTrending} from "./app/tasks/check.trending";
@Module({
imports: [ScheduleModule.forRoot()],
controllers: [],
providers: [CheckTrending],
})
export class CronModule {}

9
apps/cron/src/main.ts Normal file
View File

@ -0,0 +1,9 @@
import { NestFactory } from '@nestjs/core';
import {CronModule} from "./cron.module";
async function bootstrap() {
// some comment again
await NestFactory.createApplicationContext(CronModule);
}
bootstrap();

View File

@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["node"],
"emitDecoratorMetadata": true,
"target": "es2021"
},
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
"include": ["src/**/*.ts"]
}

16
apps/cron/tsconfig.json Normal file
View File

@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"compilerOptions": {
"esModuleInterop": true
}
}

View File

@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

View File

@ -0,0 +1,13 @@
const { composePlugins, withNx } = require('@nx/webpack');
// Nx plugins for webpack.
module.exports = composePlugins(
withNx({
target: 'node',
}),
(config) => {
// Update the webpack config as needed here.
// e.g. `config.plugins.push(new MyPlugin())`
return config;
}
);

View File

@ -1,8 +1,3 @@
/**
* This is not a production server yet!
* This is only a minimal backend to get started.
*/
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app/app.module';

View File

@ -0,0 +1,3 @@
export const isDev = () => {
return process.env.NODE_ENV === 'development' || !process.env.NODE_ENV;
}

View File

@ -24,6 +24,7 @@ export class OrganizationRepository {
password: body.password ? AuthService.hashPassword(body.password) : '',
providerName: body.provider,
providerId: body.providerId || '',
timezone: 0
}
}
}

View File

@ -11,23 +11,33 @@ datasource db {
}
model Organization {
id String @id @default(uuid())
name String
description String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
users UserOrganization[]
id String @id @default(uuid())
name String
description String?
users UserOrganization[]
media Media[]
paymentId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
github GitHub[]
subscription Subscription?
channel Channel?
tags Tag[]
postTags PostTag[]
postMedia PostMedia[]
post Post[]
}
model User {
id String @id @default(uuid())
id String @id @default(uuid())
email String
password String?
providerName Provider
providerId String?
organizations UserOrganization[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
timezone Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([email, providerName])
}
@ -43,13 +53,182 @@ model UserOrganization {
updatedAt DateTime @updatedAt
}
model GitHub {
id String @id @default(uuid())
login String
name String
token String
jobId String
organization Organization @relation(fields: [organizationId], references: [id])
organizationId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
stars Star[]
@@index([login])
}
model Trending {
id String @id @default(uuid())
login String
feed Int
language Int
date DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([login])
}
model Star {
id String @id @default(uuid())
githubId String
github GitHub @relation(fields: [githubId], references: [id])
stars Int
totalStars Int
date DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Media {
id String @id @default(uuid())
name String
url String
organization Organization @relation(fields: [organizationId], references: [id])
organizationId String
posts PostMedia[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Subscription {
id String @id @default(cuid())
organizationId String @unique
organization Organization @relation(fields: [organizationId], references: [id])
subscriptionTier SubscriptionTier
subscriptionState SubscriptionState
identifier String?
cancelAt DateTime?
period Period
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Channel {
id String @id @default(cuid())
organizationId String @unique
organization Organization @relation(fields: [organizationId], references: [id])
channelProvider ChannelProvider
type Type
token String
refreshToken String?
additionalData Json?
posts Post[]
}
model Tag {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
name String
posts PostTag[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model PostTag {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
postId String
post Post @relation(fields: [postId], references: [id])
tagId String
tag Tag @relation(fields: [tagId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model PostMedia {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
postId String
post Post @relation(fields: [postId], references: [id])
mediaId String
media Media @relation(fields: [mediaId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id String @id @default(cuid())
state State @default(QUEUE)
queueId String?
publishDate DateTime
organizationId String
channelId String
organization Organization @relation(fields: [organizationId], references: [id])
channel Channel @relation(fields: [channelId], references: [id])
title String?
description String?
canonicalUrl String?
canonicalPostId String?
parentPostId String?
canonicalPost Post? @relation("canonicalPostId", fields: [canonicalPostId], references: [id])
parentPost Post? @relation("parentPostId", fields: [parentPostId], references: [id])
canonicalChildren Post[] @relation("canonicalPostId")
childrenPost Post[] @relation("parentPostId")
tags PostTag[]
media PostMedia[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum Type {
ARTICLE
SOCIAL
}
enum State {
QUEUE
SENT
DRAFT
}
enum ChannelProvider {
TWITTER
LINKEDIN
DEV
HASHNODE
MEDIUM
HACKERNOON
YOUTUBE
GITHUB
DISCORD
}
enum SubscriptionTier {
BASIC
PRO
}
enum SubscriptionState {
ACTIVE
INACTIVE
}
enum Period {
MONTHLY
YEARLY
}
enum Provider {
LOCAL
GITHUB
LOCAL
GITHUB
}
enum Role {
SUPERADMIN
ADMIN
USER
}
}

41
package-lock.json generated
View File

@ -16,6 +16,7 @@
"@nestjs/core": "^10.0.2",
"@nestjs/microservices": "^10.3.1",
"@nestjs/platform-express": "^10.0.2",
"@nestjs/schedule": "^4.0.0",
"@prisma/client": "^5.8.1",
"@swc/helpers": "~0.5.2",
"@types/bcrypt": "^5.0.2",
@ -3581,6 +3582,32 @@
"@nestjs/core": "^10.0.0"
}
},
"node_modules/@nestjs/schedule": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-4.0.0.tgz",
"integrity": "sha512-zz4h54m/F/1qyQKvMJCRphmuwGqJltDAkFxUXCVqJBXEs5kbPt93Pza3heCQOcMH22MZNhGlc9DmDMLXVHmgVQ==",
"dependencies": {
"cron": "3.1.3",
"uuid": "9.0.1"
},
"peerDependencies": {
"@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0",
"@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0",
"reflect-metadata": "^0.1.12"
}
},
"node_modules/@nestjs/schedule/node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/@nestjs/schematics": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.0.tgz",
@ -6042,6 +6069,11 @@
"@types/node": "*"
}
},
"node_modules/@types/luxon": {
"version": "3.3.8",
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.8.tgz",
"integrity": "sha512-jYvz8UMLDgy3a5SkGJne8H7VA7zPV2Lwohjx0V8V31+SqAjNmurWMkk9cQhfvlcnXWudBpK9xPM1n4rljOcHYQ=="
},
"node_modules/@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
@ -9102,6 +9134,15 @@
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"node_modules/cron": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/cron/-/cron-3.1.3.tgz",
"integrity": "sha512-KVxeKTKYj2eNzN4ElnT6nRSbjbfhyxR92O/Jdp6SH3pc05CDJws59jBrZWEMQlxevCiE6QUTrXy+Im3vC3oD3A==",
"dependencies": {
"@types/luxon": "~3.3.0",
"luxon": "~3.4.0"
}
},
"node_modules/cron-parser": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz",

View File

@ -3,7 +3,8 @@
"version": "0.0.0",
"license": "MIT",
"scripts": {
"dev": "concurrently \"stripe listen --forward-to localhost:3000/payment\" \"nx run-many --target=serve --projects=frontend,backend,workers --parallel\"",
"dev": "concurrently \"stripe listen --forward-to localhost:3000/payment\" \"nx run-many --target=serve --projects=frontend,backend,workers,cron --parallel=4\"",
"cron": "nx run cron:serve:development",
"prisma-generate": "cd ./libraries/nestjs-libraries/src/database/prisma && prisma generate",
"prisma-db-push": "cd ./libraries/nestjs-libraries/src/database/prisma && prisma db push"
},
@ -13,6 +14,7 @@
"@nestjs/core": "^10.0.2",
"@nestjs/microservices": "^10.3.1",
"@nestjs/platform-express": "^10.0.2",
"@nestjs/schedule": "^4.0.0",
"@prisma/client": "^5.8.1",
"@swc/helpers": "~0.5.2",
"@types/bcrypt": "^5.0.2",

View File

@ -21,7 +21,8 @@
"@gitroom/frontend/*": ["apps/frontend/src/*"],
"@gitroom/nestjs-libraries/*": ["libraries/nestjs-libraries/src/*"],
"@gitroom/helpers/*": ["libraries/helpers/src/*"],
"@gitroom/workers/*": ["apps/workers/src/*"]
"@gitroom/workers/*": ["apps/workers/src/*"],
"@gitroom/cron/*": ["apps/cron/src/*"]
}
},
"exclude": ["node_modules", "tmp"]