├── apps ├── .gitkeep ├── server │ ├── src │ │ ├── app │ │ │ ├── .gitkeep │ │ │ ├── app.service.ts │ │ │ ├── app.controller.ts │ │ │ ├── app.service.spec.ts │ │ │ └── app.controller.spec.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.ts │ │ │ └── environment.prod.ts │ │ ├── utils │ │ │ └── timeout.ts │ │ ├── models │ │ │ ├── platform.model.ts │ │ │ ├── node.model.ts │ │ │ ├── collect.model.ts │ │ │ ├── errors │ │ │ │ └── user-error.model.ts │ │ │ ├── metadata.model.ts │ │ │ ├── topic.model.ts │ │ │ ├── post-tag.model.ts │ │ │ ├── color.model.ts │ │ │ ├── date.model.ts │ │ │ ├── license.model.ts │ │ │ ├── language.model.ts │ │ │ ├── connections │ │ │ │ ├── post.connections.ts │ │ │ │ ├── owner.connection.ts │ │ │ │ ├── topic.connection.ts │ │ │ │ ├── collect.connection.ts │ │ │ │ ├── license.connection.ts │ │ │ │ ├── language.connection.ts │ │ │ │ ├── repository.connection.ts │ │ │ │ └── collection.connection.ts │ │ │ ├── user.model.ts │ │ │ ├── collection.model.ts │ │ │ ├── post.model.ts │ │ │ ├── args │ │ │ │ └── platform-by-id.args.ts │ │ │ ├── submission.model.ts │ │ │ ├── report.model.ts │ │ │ ├── rgba.model.ts │ │ │ ├── enums │ │ │ │ ├── script-direction.enum.ts │ │ │ │ ├── owner-type.enum.ts │ │ │ │ ├── reportable-type.enum.ts │ │ │ │ └── platform-type.enum.ts │ │ │ ├── owner.model.ts │ │ │ └── repository.model.ts │ │ ├── language │ │ │ ├── args │ │ │ │ ├── language-slug.args.ts │ │ │ │ ├── language-id.args.ts │ │ │ │ └── language-order.args.ts │ │ │ ├── language.module.ts │ │ │ └── enums │ │ │ │ └── language-order.enum.ts │ │ ├── collection │ │ │ ├── args │ │ │ │ ├── collection-slug.args.ts │ │ │ │ └── collection-id.args.ts │ │ │ ├── collection.processor.ts │ │ │ ├── collection.scheduler.ts │ │ │ ├── collection.command.ts │ │ │ ├── collection.module.ts │ │ │ └── collection.controller.ts │ │ ├── repository │ │ │ ├── args │ │ │ │ ├── repo-search.args.ts │ │ │ │ ├── repo-order.args.ts │ │ │ │ ├── platform.args.ts │ │ │ │ └── repo-filter.args.ts │ │ │ ├── repository.module.ts │ │ │ └── enums │ │ │ │ ├── fork-status-type.enum.ts │ │ │ │ ├── archive-status-type.enum.ts │ │ │ │ ├── template-status-type.enum.ts │ │ │ │ └── repos-order.enum.ts │ │ ├── date │ │ │ ├── args │ │ │ │ ├── difference.args.ts │ │ │ │ └── formatted.args.ts │ │ │ ├── utils.ts │ │ │ ├── date.module.ts │ │ │ └── date.resolver.ts │ │ ├── collect │ │ │ ├── args │ │ │ │ └── collect-id.args.ts │ │ │ ├── collect.module.ts │ │ │ └── collect.resolver.ts │ │ ├── post │ │ │ ├── args │ │ │ │ └── post-filter.args.ts │ │ │ └── post.module.ts │ │ ├── user │ │ │ └── user.module.ts │ │ ├── color │ │ │ ├── color.module.ts │ │ │ ├── color.resolver.ts │ │ │ └── utils.ts │ │ ├── owner │ │ │ ├── owner.module.ts │ │ │ ├── constants.ts │ │ │ ├── args │ │ │ │ ├── owner-order.args.ts │ │ │ │ ├── platform.args.ts │ │ │ │ └── owner-filter.args.ts │ │ │ └── enums │ │ │ │ └── owner-order.enum.ts │ │ ├── topic │ │ │ ├── topic.module.ts │ │ │ ├── args │ │ │ │ └── topic-order.args.ts │ │ │ └── enums │ │ │ │ └── topics-order.enum.ts │ │ ├── report │ │ │ ├── report.module.ts │ │ │ ├── reportable.resolver.ts │ │ │ └── report.resolver.ts │ │ ├── license │ │ │ ├── license.module.ts │ │ │ ├── args │ │ │ │ └── license-order.args.ts │ │ │ └── enums │ │ │ │ └── license-order.enum.ts │ │ ├── post-tag │ │ │ ├── post-tag.module.ts │ │ │ └── post-tag.resolver.ts │ │ ├── cli.ts │ │ ├── markdown │ │ │ └── markdown.module.ts │ │ ├── admin │ │ │ └── resources │ │ │ │ ├── user.resource.ts │ │ │ │ ├── report.resource.ts │ │ │ │ ├── topic.resource.ts │ │ │ │ ├── language.resource.ts │ │ │ │ ├── license.resource.ts │ │ │ │ ├── post-tag.resource.ts │ │ │ │ ├── collect.resource.ts │ │ │ │ ├── resource-type.ts │ │ │ │ ├── collection.resource.ts │ │ │ │ └── index.ts │ │ ├── metadata │ │ │ └── metadata.module.ts │ │ ├── plugins │ │ │ ├── pagination-complexity.ts │ │ │ └── complexity.plugin.ts │ │ ├── submission │ │ │ ├── submission.module.ts │ │ │ └── submission.payload.ts │ │ ├── cache │ │ │ └── cache.module.ts │ │ ├── github-discoverer │ │ │ ├── repo-discovery-terms.ts │ │ │ ├── github-discoverer.scheduler.ts │ │ │ ├── github-discoverer.command.ts │ │ │ └── github-discoverer.module.ts │ │ ├── hybrid-throttler.guard.ts │ │ ├── main.ts │ │ ├── github-extractor │ │ │ ├── github-extractor.scheduler.ts │ │ │ ├── github-extractor.processor.ts │ │ │ ├── github-extractor.module.ts │ │ │ └── github-extractor.command.ts │ │ └── queue.ts │ ├── Dockerfile │ ├── tsconfig.json │ ├── tsconfig.spec.json │ ├── tsconfig.app.json │ ├── jest.config.js │ ├── .eslintrc.json │ └── webpack.config.js ├── web │ ├── public │ │ ├── .gitkeep │ │ ├── favicon.ico │ │ └── sponsors │ │ │ ├── empty-dark.png │ │ │ └── empty-light.png │ ├── graphql │ │ ├── fragments │ │ │ ├── post-full.fragment.graphql │ │ │ ├── post-preview.fragment.graphql │ │ │ ├── repo-preview.fragment.graphql │ │ │ └── repo-full.fragment.graphql │ │ ├── queries │ │ │ ├── metadata.graphql │ │ │ ├── get-languages.graphql │ │ │ ├── get-searched-repositories.graphql │ │ │ ├── get-tag.graphql │ │ │ ├── get-post.graphql │ │ │ ├── get-collections.graphql │ │ │ ├── get-repository.graphql │ │ │ ├── get-owner.graphql │ │ │ ├── get-collection.graphql │ │ │ ├── get-posts.graphql │ │ │ ├── get-github-owners.graphql │ │ │ └── get-repositories.graphql │ │ └── mutations │ │ │ ├── report.graphql │ │ │ └── send-submission.graphql │ ├── postcss.config.js │ ├── index.d.ts │ ├── next-env.d.ts │ ├── Dockerfile │ ├── specs │ │ └── index.spec.tsx │ ├── components │ │ ├── SkeletonLoader │ │ │ ├── SkeletonLoaderShape.module.css │ │ │ ├── LanguagesFilterSkeletonLoader.tsx │ │ │ ├── SkeletonLoaderShape.tsx │ │ │ └── PostPreviewSkeletonLoader.tsx │ │ ├── UI │ │ │ ├── Button │ │ │ │ ├── GhostButton.tsx │ │ │ │ ├── OutlineButton.tsx │ │ │ │ ├── RedButton.tsx │ │ │ │ └── PrimaryButton.tsx │ │ │ ├── Divider.tsx │ │ │ ├── Input │ │ │ │ ├── Input.tsx │ │ │ │ ├── TextareaInput.tsx │ │ │ │ └── TextInput.tsx │ │ │ ├── IconButton.tsx │ │ │ ├── Expandable.tsx │ │ │ ├── RadioList.tsx │ │ │ └── Card.tsx │ │ ├── Feature │ │ │ ├── ClientOnlyPortal.tsx │ │ │ └── InfiniteScroll.tsx │ │ ├── Icons │ │ │ └── MatnbazLogo.tsx │ │ ├── Layout │ │ │ └── PageHeader.tsx │ │ ├── Modal │ │ │ └── SubmitUserModal.tsx │ │ ├── Report │ │ │ ├── OwnerReport.tsx │ │ │ └── RepositoryReport.tsx │ │ ├── Repository │ │ │ └── RepositoryPreviewFromGithub.tsx │ │ ├── Owner │ │ │ └── OwnerImage.tsx │ │ └── User │ │ │ └── UserPreview.tsx │ ├── jest.config.js │ ├── next-sitemap.js │ ├── tsconfig.spec.json │ ├── tsconfig.json │ ├── .eslintrc.json │ ├── styles │ │ └── global.css │ ├── pages │ │ ├── submit-user.tsx │ │ ├── 404.tsx │ │ ├── about.tsx │ │ ├── 500.tsx │ │ ├── blog │ │ │ └── index.tsx │ │ ├── _app.tsx │ │ └── faq.tsx │ ├── next.config.js │ ├── lib │ │ ├── apollo-client.ts │ │ └── apollo.ts │ ├── hooks │ │ └── use-click-outside.ts │ ├── tailwind.config.js │ └── utils │ │ └── gradient-util.ts ├── github-bot │ ├── src │ │ ├── app │ │ │ ├── .gitkeep │ │ │ ├── app.controller.ts │ │ │ ├── app.service.spec.ts │ │ │ ├── app.controller.spec.ts │ │ │ └── app.module.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.ts │ │ │ └── environment.prod.ts │ │ └── main.ts │ ├── tsconfig.json │ ├── tsconfig.spec.json │ ├── tsconfig.app.json │ ├── .eslintrc.json │ ├── jest.config.js │ └── project.json ├── image-generator │ ├── src │ │ ├── app │ │ │ ├── .gitkeep │ │ │ ├── app.service.ts │ │ │ ├── app.controller.ts │ │ │ ├── app.module.ts │ │ │ ├── app.service.spec.ts │ │ │ └── app.controller.spec.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.ts │ │ │ └── environment.prod.ts │ │ ├── post │ │ │ ├── post.module.ts │ │ │ └── post.controller.ts │ │ └── main.ts │ ├── tsconfig.json │ ├── tsconfig.spec.json │ ├── tsconfig.app.json │ ├── .eslintrc.json │ ├── jest.config.js │ └── project.json └── web-e2e │ ├── src │ ├── support │ │ ├── app.po.ts │ │ ├── index.ts │ │ └── commands.ts │ ├── fixtures │ │ └── example.json │ └── integration │ │ └── app.spec.ts │ ├── tsconfig.json │ ├── .eslintrc.json │ ├── cypress.json │ └── project.json ├── libs ├── .gitkeep ├── common │ ├── src │ │ ├── lib │ │ │ ├── timer │ │ │ │ ├── index.ts │ │ │ │ └── timer.ts │ │ │ ├── random │ │ │ │ ├── index.ts │ │ │ │ └── random.ts │ │ │ ├── slugify │ │ │ │ ├── index.ts │ │ │ │ ├── slugify.ts │ │ │ │ └── slugify.spec.ts │ │ │ ├── repo-requirements.ts │ │ │ ├── word-limiter │ │ │ │ ├── index.ts │ │ │ │ └── word-limiter.ts │ │ │ ├── guess-direction │ │ │ │ ├── index.ts │ │ │ │ ├── guess-direction.ts │ │ │ │ └── guess-direction.spec.ts │ │ │ ├── persian-numbers │ │ │ │ ├── index.ts │ │ │ │ ├── persian-numbers.spec.ts │ │ │ │ └── persian-numbers.ts │ │ │ ├── null-to-undefined │ │ │ │ ├── index.ts │ │ │ │ ├── null-to-undefined.ts │ │ │ │ └── null-to-undefined.spec.ts │ │ │ ├── humanly-readable-date │ │ │ │ ├── index.ts │ │ │ │ ├── humanly-readable-date.ts │ │ │ │ └── humanly-readable-date.spec.ts │ │ │ └── links.ts │ │ └── index.ts │ ├── .babelrc │ ├── README.md │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── .eslintrc.json │ ├── jest.config.js │ ├── tsconfig.spec.json │ └── project.json ├── telegraf │ ├── src │ │ ├── lib │ │ │ ├── interfaces │ │ │ │ ├── index.ts │ │ │ │ └── telegraf-module-options.interface.ts │ │ │ └── telegraf.constants.ts │ │ └── index.ts │ ├── .babelrc │ ├── README.md │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── .eslintrc.json │ ├── jest.config.js │ ├── tsconfig.spec.json │ └── project.json └── monomedia │ ├── .babelrc │ ├── src │ ├── lib │ │ ├── interfaces │ │ │ ├── index.ts │ │ │ └── monomedia-module-options.interface.ts │ │ ├── utils.ts │ │ ├── monomedia.constants.ts │ │ └── monomedia.service.spec.ts │ └── index.ts │ ├── README.md │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── .eslintrc.json │ ├── jest.config.js │ ├── tsconfig.spec.json │ └── project.json ├── storage ├── chromium │ └── .gitkeep └── instagram │ └── .gitkeep ├── tools ├── generators │ └── .gitkeep └── tsconfig.tools.json ├── .prettierrc ├── babel.config.json ├── .dockerignore ├── .vscode ├── settings.json └── extensions.json ├── .prettierignore ├── jest.preset.js ├── prisma ├── migrations │ ├── 20220129125130_add_name_to_owner │ │ └── migration.sql │ ├── 20220112085933_add_og_image_url_to_repo │ │ └── migration.sql │ ├── migration_lock.toml │ ├── 20220129134947_drop_public_contributions_default │ │ └── migration.sql │ ├── 20220111121157_add_latest_extraction_date │ │ └── migration.sql │ ├── 20220110124549_remove_discovery_terms │ │ └── migration.sql │ ├── 20220129100115_add_owner_fields │ │ └── migration.sql │ ├── 20220103122333_added_trend_indicators │ │ └── migration.sql │ ├── 20220127155935_connect_user_and_owners │ │ └── migration.sql │ ├── 20220129134338_add_public_contributions │ │ └── migration.sql │ ├── 20220109114338_add_owner_statistic │ │ └── migration.sql │ ├── 20220127160100_add_username_and_profile_to_user │ │ └── migration.sql │ ├── 20220111055624_add_node_id_to_repo_and_owner │ │ └── migration.sql │ ├── 20220127155742_remove_selections │ │ └── migration.sql │ ├── 20220111151256_remove_irrelevant_fields │ │ └── migration.sql │ ├── 20220120114153_add_selections │ │ └── migration.sql │ └── 20220127155757_add_posts_and_tags │ │ └── migration.sql └── seed.ts ├── jest.config.js ├── Dockerfile ├── CONTRIBUTING.md ├── .editorconfig ├── codegen.yml ├── workspace.json ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── tsconfig.base.json ├── .eslintrc.json ├── nx.json ├── .env.example ├── FAQ.md └── docker-compose.yml /apps/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/server/src/app/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/web/public/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /storage/chromium/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /storage/instagram/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/generators/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/github-bot/src/app/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/server/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/github-bot/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/image-generator/src/app/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/image-generator/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "babelrcRoots": ["*"] 3 | } 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | tmp 4 | .vscode 5 | -------------------------------------------------------------------------------- /libs/common/src/lib/timer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './timer'; 2 | -------------------------------------------------------------------------------- /libs/common/src/lib/random/index.ts: -------------------------------------------------------------------------------- 1 | export * from './random'; 2 | -------------------------------------------------------------------------------- /libs/common/src/lib/slugify/index.ts: -------------------------------------------------------------------------------- 1 | export * from './slugify'; 2 | -------------------------------------------------------------------------------- /libs/common/src/lib/repo-requirements.ts: -------------------------------------------------------------------------------- 1 | export const MINIMUM_STARS = 4; 2 | -------------------------------------------------------------------------------- /libs/common/src/lib/word-limiter/index.ts: -------------------------------------------------------------------------------- 1 | export * from './word-limiter'; 2 | -------------------------------------------------------------------------------- /libs/common/src/lib/guess-direction/index.ts: -------------------------------------------------------------------------------- 1 | export * from './guess-direction'; 2 | -------------------------------------------------------------------------------- /libs/common/src/lib/persian-numbers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './persian-numbers'; 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "conventionalCommits.scopes": ["Server", "Web"] 3 | } 4 | -------------------------------------------------------------------------------- /apps/web-e2e/src/support/app.po.ts: -------------------------------------------------------------------------------- 1 | export const getGreeting = () => cy.get('h1'); 2 | -------------------------------------------------------------------------------- /libs/common/src/lib/null-to-undefined/index.ts: -------------------------------------------------------------------------------- 1 | export * from './null-to-undefined'; 2 | -------------------------------------------------------------------------------- /libs/common/src/lib/humanly-readable-date/index.ts: -------------------------------------------------------------------------------- 1 | export * from './humanly-readable-date'; 2 | -------------------------------------------------------------------------------- /libs/telegraf/src/lib/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './telegraf-module-options.interface'; 2 | -------------------------------------------------------------------------------- /libs/common/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/monomedia/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]] 3 | } 4 | -------------------------------------------------------------------------------- /libs/monomedia/src/lib/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './monomedia-module-options.interface'; 2 | -------------------------------------------------------------------------------- /libs/telegraf/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]] 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | 3 | /dist 4 | /coverage 5 | -------------------------------------------------------------------------------- /apps/server/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | }; 4 | -------------------------------------------------------------------------------- /jest.preset.js: -------------------------------------------------------------------------------- 1 | const nxPreset = require('@nrwl/jest/preset'); 2 | 3 | module.exports = { ...nxPreset }; 4 | -------------------------------------------------------------------------------- /apps/github-bot/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | }; 4 | -------------------------------------------------------------------------------- /apps/server/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /apps/web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamidreza01/matnbaz/HEAD/apps/web/public/favicon.ico -------------------------------------------------------------------------------- /apps/github-bot/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /apps/image-generator/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | }; 4 | -------------------------------------------------------------------------------- /apps/image-generator/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /apps/web/graphql/fragments/post-full.fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment postFull on Post { 2 | ...postPreview 3 | contentHtml 4 | } 5 | -------------------------------------------------------------------------------- /apps/web/public/sponsors/empty-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamidreza01/matnbaz/HEAD/apps/web/public/sponsors/empty-dark.png -------------------------------------------------------------------------------- /apps/server/src/app/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService {} 5 | -------------------------------------------------------------------------------- /apps/web-e2e/src/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io" 4 | } 5 | -------------------------------------------------------------------------------- /apps/web/public/sponsors/empty-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamidreza01/matnbaz/HEAD/apps/web/public/sponsors/empty-light.png -------------------------------------------------------------------------------- /prisma/migrations/20220129125130_add_name_to_owner/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Owner" ADD COLUMN "name" TEXT; 3 | -------------------------------------------------------------------------------- /apps/server/src/utils/timeout.ts: -------------------------------------------------------------------------------- 1 | export const timeout = (ms) => { 2 | return new Promise((resolve) => setTimeout(resolve, ms)); 3 | }; 4 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const { getJestProjects } = require('@nrwl/jest'); 2 | 3 | module.exports = { 4 | projects: getJestProjects(), 5 | }; 6 | -------------------------------------------------------------------------------- /libs/telegraf/src/lib/telegraf.constants.ts: -------------------------------------------------------------------------------- 1 | export const TELEGRAF = 'TELEGRAF'; 2 | export const TELEGRAF_OPTIONS = 'TELEGRAF_OPTIONS'; 3 | -------------------------------------------------------------------------------- /libs/telegraf/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/interfaces'; 2 | export * from './lib/telegraf.constants'; 3 | export * from './lib/telegraf.module'; 4 | -------------------------------------------------------------------------------- /libs/common/src/lib/random/random.ts: -------------------------------------------------------------------------------- 1 | export const randomBetween = (min: number, max: number) => 2 | Math.floor(Math.random() * (max - min + 1) + min); 3 | -------------------------------------------------------------------------------- /libs/monomedia/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | export const normalizeTelegramUsername = (username: string) => 2 | (username.startsWith('@') ? '' : '@') + username; 3 | -------------------------------------------------------------------------------- /apps/web/graphql/queries/metadata.graphql: -------------------------------------------------------------------------------- 1 | query Metadata { 2 | metadata { 3 | totalOwnersCount 4 | totalReposCount 5 | totalTopicsCount 6 | } 7 | } -------------------------------------------------------------------------------- /prisma/migrations/20220112085933_add_og_image_url_to_repo/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Repository" ADD COLUMN "openGraphImageUrl" TEXT; 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 AS builder 2 | 3 | # Production use node instead of root 4 | # USER node 5 | 6 | WORKDIR /app/builder 7 | COPY . . 8 | RUN yarn install 9 | -------------------------------------------------------------------------------- /apps/server/src/models/platform.model.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '@nestjs/graphql'; 2 | 3 | @ObjectType() 4 | export class Platform { 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /apps/web/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: { config: './apps/web/tailwind.config.js' }, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/server/src/language/args/language-slug.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType } from '@nestjs/graphql'; 2 | 3 | @ArgsType() 4 | export class LanguageSlugArgs { 5 | slug: string; 6 | } 7 | -------------------------------------------------------------------------------- /libs/common/src/lib/null-to-undefined/null-to-undefined.ts: -------------------------------------------------------------------------------- 1 | export const nullToUndefined = (value: T, callback = (value) => value) => 2 | value ? callback(value) : undefined; 3 | -------------------------------------------------------------------------------- /apps/server/src/collection/args/collection-slug.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType } from '@nestjs/graphql'; 2 | 3 | @ArgsType() 4 | export class CollectionSlugArgs { 5 | slug: string; 6 | } 7 | -------------------------------------------------------------------------------- /apps/server/src/repository/args/repo-search.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType } from '@nestjs/graphql'; 2 | 3 | @ArgsType() 4 | export class RepoSearchArgs { 5 | searchTerm?: string; 6 | } 7 | -------------------------------------------------------------------------------- /prisma/migrations/20220129134947_drop_public_contributions_default/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "OwnerStatistic" ALTER COLUMN "publicContributionsCount" DROP DEFAULT; 3 | -------------------------------------------------------------------------------- /apps/server/src/date/args/difference.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType } from '@nestjs/graphql'; 2 | 3 | @ArgsType() 4 | export class DifferenceArgs { 5 | persianNumbers: boolean = true; 6 | } 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Please state the feature or bug as an issue and start contributing **after** the approval of the maintainers. 2 | 3 | We'd hate to close your PRs. 4 | 5 | That's all for now. 6 | -------------------------------------------------------------------------------- /apps/server/src/models/node.model.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, InterfaceType } from '@nestjs/graphql'; 2 | 3 | @InterfaceType() 4 | export class Node { 5 | @Field(() => ID) 6 | id: string; 7 | } 8 | -------------------------------------------------------------------------------- /apps/server/src/models/collect.model.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '@nestjs/graphql'; 2 | import { Node } from './node.model'; 3 | 4 | @ObjectType({ implements: [Node] }) 5 | export class Collect {} 6 | -------------------------------------------------------------------------------- /apps/server/src/models/errors/user-error.model.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '@nestjs/graphql'; 2 | 3 | @ObjectType() 4 | export class UserError { 5 | message: string; 6 | path?: string[]; 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/graphql/mutations/report.graphql: -------------------------------------------------------------------------------- 1 | mutation Report($subject: ReportableType!, $id: ID!, $reason: String!) { 2 | report(subject: $subject, id: $id, reason: $reason) { 3 | id 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /libs/monomedia/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/interfaces'; 2 | export * from './lib/monomedia.constants'; 3 | export * from './lib/monomedia.module'; 4 | export * from './lib/monomedia.service'; 5 | -------------------------------------------------------------------------------- /apps/server/src/collect/args/collect-id.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field, ID } from '@nestjs/graphql'; 2 | 3 | @ArgsType() 4 | export class CollectIdArgs { 5 | @Field(() => ID) 6 | id: string; 7 | } 8 | -------------------------------------------------------------------------------- /apps/server/src/date/utils.ts: -------------------------------------------------------------------------------- 1 | import { DateObject } from '../models/date.model'; 2 | 3 | export const createDateObject = (date: Date): DateObject => { 4 | return { 5 | original: date, 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /apps/server/src/language/args/language-id.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field, ID } from '@nestjs/graphql'; 2 | 3 | @ArgsType() 4 | export class LanguageIdArgs { 5 | @Field(() => ID) 6 | id: string; 7 | } 8 | -------------------------------------------------------------------------------- /apps/server/src/models/metadata.model.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '@nestjs/graphql'; 2 | 3 | @ObjectType() 4 | export class Metadata { 5 | totalReposCount: number; 6 | totalOwnersCount: number; 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/index.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | declare module '*.svg' { 3 | const content: any; 4 | export const ReactComponent: any; 5 | export default content; 6 | } 7 | -------------------------------------------------------------------------------- /libs/common/src/lib/guess-direction/guess-direction.ts: -------------------------------------------------------------------------------- 1 | export const guessDirection = (text: string) => 2 | text.match(/[\u0600-\u06FF\uFB8A\u067E\u0686\u06AF\u200C\u200F]/g) 3 | ? 'RTL' 4 | : 'LTR'; 5 | -------------------------------------------------------------------------------- /apps/server/src/collection/args/collection-id.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field, ID } from '@nestjs/graphql'; 2 | 3 | @ArgsType() 4 | export class CollectionIdArgs { 5 | @Field(() => ID) 6 | id: string; 7 | } 8 | -------------------------------------------------------------------------------- /libs/common/README.md: -------------------------------------------------------------------------------- 1 | # common 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test common` to execute the unit tests via [Jest](https://jestjs.io). 8 | -------------------------------------------------------------------------------- /apps/server/src/date/date.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { DateResolver } from './date.resolver'; 3 | 4 | @Module({ 5 | providers: [DateResolver], 6 | }) 7 | export class DateModule {} 8 | -------------------------------------------------------------------------------- /apps/server/src/post/args/post-filter.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType } from '@nestjs/graphql'; 2 | 3 | @ArgsType() 4 | export class PostFilterArgs { 5 | /** 6 | * Tags 7 | */ 8 | tags?: string[]; 9 | } 10 | -------------------------------------------------------------------------------- /apps/server/src/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { UserResolver } from './user.resolver'; 3 | 4 | @Module({ 5 | providers: [UserResolver], 6 | }) 7 | export class UserModule {} 8 | -------------------------------------------------------------------------------- /libs/telegraf/README.md: -------------------------------------------------------------------------------- 1 | # telegraf 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test telegraf` to execute the unit tests via [Jest](https://jestjs.io). 8 | -------------------------------------------------------------------------------- /apps/server/src/color/color.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ColorResolver } from './color.resolver'; 3 | 4 | @Module({ 5 | providers: [ColorResolver], 6 | }) 7 | export class ColorModule {} 8 | -------------------------------------------------------------------------------- /apps/server/src/date/args/formatted.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType } from '@nestjs/graphql'; 2 | 3 | @ArgsType() 4 | export class FormattedArgs { 5 | format: string = 'd MMMM yyyy'; 6 | persianNumbers: boolean = true; 7 | } 8 | -------------------------------------------------------------------------------- /apps/server/src/models/topic.model.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '@nestjs/graphql'; 2 | import { Node } from './node.model'; 3 | 4 | @ObjectType({ implements: [Node] }) 5 | export class Topic { 6 | name: string; 7 | } 8 | -------------------------------------------------------------------------------- /apps/server/src/owner/owner.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { OwnerResolver } from './owner.resolver'; 3 | 4 | @Module({ 5 | providers: [OwnerResolver], 6 | }) 7 | export class OwnerModule {} 8 | -------------------------------------------------------------------------------- /apps/server/src/topic/topic.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TopicResolver } from './topic.resolver'; 3 | 4 | @Module({ 5 | providers: [TopicResolver], 6 | }) 7 | export class TopicModule {} 8 | -------------------------------------------------------------------------------- /libs/monomedia/README.md: -------------------------------------------------------------------------------- 1 | # monomedia 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test monomedia` to execute the unit tests via [Jest](https://jestjs.io). 8 | -------------------------------------------------------------------------------- /apps/server/src/models/post-tag.model.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '@nestjs/graphql'; 2 | import { Node } from './node.model'; 3 | 4 | @ObjectType({ implements: [Node] }) 5 | export class PostTag { 6 | name: string; 7 | } 8 | -------------------------------------------------------------------------------- /libs/common/src/lib/humanly-readable-date/humanly-readable-date.ts: -------------------------------------------------------------------------------- 1 | import { format } from 'date-fns-jalali'; 2 | 3 | export const humanlyReadableDate = (date: number | Date) => { 4 | return format(date, 'd MMMM yyyy'); 5 | }; 6 | -------------------------------------------------------------------------------- /apps/server/src/report/report.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ReportResolver } from './report.resolver'; 3 | 4 | @Module({ 5 | providers: [ReportResolver], 6 | }) 7 | export class ReportModule {} 8 | -------------------------------------------------------------------------------- /apps/server/src/collect/collect.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { CollectResolver } from './collect.resolver'; 3 | 4 | @Module({ 5 | providers: [CollectResolver], 6 | }) 7 | export class CollectModule {} 8 | -------------------------------------------------------------------------------- /apps/server/src/license/license.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { LicenseResolver } from './license.resolver'; 3 | 4 | @Module({ 5 | providers: [LicenseResolver], 6 | }) 7 | export class LicenseModule {} 8 | -------------------------------------------------------------------------------- /apps/server/src/owner/constants.ts: -------------------------------------------------------------------------------- 1 | export enum OwnerReason { 2 | REPOSITORY_SEARCH = 'REPOSITORY_SEARCH', 3 | USER_SEARCH = 'USER_SEARCH', 4 | ORGANIZATION_PRESENCE = 'ORGANIZATION_PRESENCE', 5 | SUBMISSION = 'SUBMISSION', 6 | } 7 | -------------------------------------------------------------------------------- /apps/server/src/post-tag/post-tag.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { PostTagResolver } from './post-tag.resolver'; 3 | 4 | @Module({ 5 | providers: [PostTagResolver], 6 | }) 7 | export class PostTagModule {} 8 | -------------------------------------------------------------------------------- /apps/web/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /libs/monomedia/src/lib/monomedia.constants.ts: -------------------------------------------------------------------------------- 1 | export const MONOMEDIA_OPTIONS = 'MONOMEDIA_OPTIONS'; 2 | export const DISCORD_WEBHOOK = 'DISCORD_WEBHOOK'; 3 | export const TELEGRAF = 'TELEGRAF'; 4 | export const INSTAGRAM = 'INSTAGRAM'; 5 | -------------------------------------------------------------------------------- /apps/server/src/language/language.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { LanguageResolver } from './language.resolver'; 3 | 4 | @Module({ 5 | providers: [LanguageResolver], 6 | }) 7 | export class LanguageModule {} 8 | -------------------------------------------------------------------------------- /apps/web/graphql/queries/get-languages.graphql: -------------------------------------------------------------------------------- 1 | query GetLanguages { 2 | languages { 3 | edges { 4 | node { 5 | id 6 | name 7 | slug 8 | repositoriesCount 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /apps/image-generator/src/app/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getData(): { message: string } { 6 | return { message: 'Welcome to image-generator!' }; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/server/src/app/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | } 8 | -------------------------------------------------------------------------------- /apps/server/src/cli.ts: -------------------------------------------------------------------------------- 1 | import { CommandFactory } from 'nest-commander'; 2 | import { AppModule } from './app/app.module'; 3 | 4 | async function bootstrap() { 5 | await CommandFactory.run(AppModule, ['error', 'warn', 'log']); 6 | } 7 | 8 | bootstrap(); 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "nrwl.angular-console", 4 | "esbenp.prettier-vscode", 5 | "firsttris.vscode-jest-runner", 6 | "dbaeumer.vscode-eslint", 7 | "vivaxy.vscode-conventional-commits" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /apps/image-generator/src/app/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | } 8 | -------------------------------------------------------------------------------- /prisma/migrations/20220111121157_add_latest_extraction_date/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Owner" ADD COLUMN "latestExtractionAt" TIMESTAMP(3); 3 | 4 | -- AlterTable 5 | ALTER TABLE "Repository" ADD COLUMN "latestExtractionAt" TIMESTAMP(3); 6 | -------------------------------------------------------------------------------- /apps/server/src/markdown/markdown.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MarkdownService } from './markdown.service'; 3 | 4 | @Module({ 5 | providers: [MarkdownService], 6 | exports: [MarkdownService], 7 | }) 8 | export class MarkdownModule {} 9 | -------------------------------------------------------------------------------- /apps/server/src/models/color.model.ts: -------------------------------------------------------------------------------- 1 | import { CacheControl } from '@exonest/graphql-cache-control'; 2 | import { ObjectType } from '@nestjs/graphql'; 3 | 4 | @CacheControl({ inheritMaxAge: true }) 5 | @ObjectType() 6 | export class Color { 7 | hexString: string; 8 | } 9 | -------------------------------------------------------------------------------- /apps/server/src/models/date.model.ts: -------------------------------------------------------------------------------- 1 | import { CacheControl } from '@exonest/graphql-cache-control'; 2 | import { ObjectType } from '@nestjs/graphql'; 3 | 4 | @CacheControl({ inheritMaxAge: true }) 5 | @ObjectType() 6 | export class DateObject { 7 | original: Date; 8 | } 9 | -------------------------------------------------------------------------------- /apps/server/src/models/license.model.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '@nestjs/graphql'; 2 | import { Node } from './node.model'; 3 | 4 | @ObjectType({ implements: [Node] }) 5 | export class License { 6 | name: string; 7 | key: string; 8 | spdxId: string; 9 | } 10 | -------------------------------------------------------------------------------- /apps/server/src/owner/args/owner-order.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field } from '@nestjs/graphql'; 2 | import { OwnerOrder } from '../enums/owner-order.enum'; 3 | 4 | @ArgsType() 5 | export class OwnerOrderArgs { 6 | @Field(() => OwnerOrder) 7 | order?: OwnerOrder; 8 | } 9 | -------------------------------------------------------------------------------- /apps/server/src/repository/args/repo-order.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field } from '@nestjs/graphql'; 2 | import { RepoOrder } from '../enums/repos-order.enum'; 3 | 4 | @ArgsType() 5 | export class RepoOrderArgs { 6 | @Field(() => RepoOrder) 7 | order?: RepoOrder; 8 | } 9 | -------------------------------------------------------------------------------- /apps/server/src/topic/args/topic-order.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field } from '@nestjs/graphql'; 2 | import { TopicOrder } from '../enums/topics-order.enum'; 3 | 4 | @ArgsType() 5 | export class TopicOrderArgs { 6 | @Field(() => TopicOrder) 7 | order?: TopicOrder; 8 | } 9 | -------------------------------------------------------------------------------- /prisma/migrations/20220110124549_remove_discovery_terms/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `DiscoveryTerm` table. If the table is not empty, all the data it contains will be lost. 5 | 6 | */ 7 | -- DropTable 8 | DROP TABLE "DiscoveryTerm"; 9 | -------------------------------------------------------------------------------- /apps/server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM matnbaz:base AS builder 2 | 3 | WORKDIR /app/builder 4 | COPY . . 5 | RUN yarn build server --prod 6 | 7 | FROM matnbaz:base 8 | 9 | WORKDIR /app/server 10 | COPY --from=builder /app/builder ./ 11 | 12 | CMD [ "node", "dist/apps/server/main.js" ] -------------------------------------------------------------------------------- /apps/server/src/models/language.model.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '@nestjs/graphql'; 2 | import { Node } from './node.model'; 3 | 4 | @ObjectType({ implements: [Node] }) 5 | export class Language { 6 | name: string; 7 | slug: string; 8 | description?: string; 9 | } 10 | -------------------------------------------------------------------------------- /apps/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.app.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM matnbaz:base AS builder 2 | 3 | WORKDIR /app/builder 4 | COPY . . 5 | RUN yarn build web --prod 6 | 7 | FROM matnbaz:base 8 | 9 | WORKDIR /app/web 10 | COPY --from=builder /app/builder ./ 11 | 12 | CMD [ "yarn", "nx", "serve", "web", "--prod" ] 13 | -------------------------------------------------------------------------------- /libs/common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/github-bot/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.app.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/server/src/admin/resources/user.resource.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from './resource-type'; 2 | 3 | export const userResource: Resource = ({ dmmf, prisma }) => ({ 4 | resource: { 5 | model: dmmf.modelMap.User, 6 | client: prisma, 7 | }, 8 | options: {}, 9 | }); 10 | -------------------------------------------------------------------------------- /apps/server/src/license/args/license-order.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field } from '@nestjs/graphql'; 2 | import { LicenseOrder } from '../enums/license-order.enum'; 3 | 4 | @ArgsType() 5 | export class LicenseOrderArgs { 6 | @Field(() => LicenseOrder) 7 | order?: LicenseOrder; 8 | } 9 | -------------------------------------------------------------------------------- /apps/server/src/models/connections/post.connections.ts: -------------------------------------------------------------------------------- 1 | import { Paginated } from '@exonest/graphql-connections'; 2 | import { ObjectType } from '@nestjs/graphql'; 3 | import { Post } from '../post.model'; 4 | 5 | @ObjectType() 6 | export class PostConnection extends Paginated(Post) {} 7 | -------------------------------------------------------------------------------- /apps/server/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/monomedia/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /libs/telegraf/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/github-bot/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/image-generator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.app.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/server/src/admin/resources/report.resource.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from './resource-type'; 2 | 3 | export const reportResource: Resource = ({ dmmf, prisma }) => ({ 4 | resource: { 5 | model: dmmf.modelMap.Report, 6 | client: prisma, 7 | }, 8 | options: {}, 9 | }); 10 | -------------------------------------------------------------------------------- /apps/server/src/admin/resources/topic.resource.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from './resource-type'; 2 | 3 | export const topicResource: Resource = ({ dmmf, prisma }) => ({ 4 | resource: { 5 | model: dmmf.modelMap.Topic, 6 | client: prisma, 7 | }, 8 | options: {}, 9 | }); 10 | -------------------------------------------------------------------------------- /apps/server/src/models/connections/owner.connection.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '@nestjs/graphql'; 2 | import { Paginated } from '@exonest/graphql-connections'; 3 | import { Owner } from '../owner.model'; 4 | 5 | @ObjectType() 6 | export class OwnerConnection extends Paginated(Owner) {} 7 | -------------------------------------------------------------------------------- /apps/server/src/models/connections/topic.connection.ts: -------------------------------------------------------------------------------- 1 | import { Paginated } from '@exonest/graphql-connections'; 2 | import { ObjectType } from '@nestjs/graphql'; 3 | import { Topic } from '../topic.model'; 4 | 5 | @ObjectType() 6 | export class TopicConnection extends Paginated(Topic) {} 7 | -------------------------------------------------------------------------------- /apps/image-generator/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/server/src/admin/resources/language.resource.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from './resource-type'; 2 | 3 | export const languageResource: Resource = ({ dmmf, prisma }) => ({ 4 | resource: { 5 | model: dmmf.modelMap.Language, 6 | client: prisma, 7 | }, 8 | options: {}, 9 | }); 10 | -------------------------------------------------------------------------------- /apps/server/src/admin/resources/license.resource.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from './resource-type'; 2 | 3 | export const licenseResource: Resource = ({ dmmf, prisma }) => ({ 4 | resource: { 5 | model: dmmf.modelMap.License, 6 | client: prisma, 7 | }, 8 | options: {}, 9 | }); 10 | -------------------------------------------------------------------------------- /apps/server/src/admin/resources/post-tag.resource.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from './resource-type'; 2 | 3 | export const postTagResource: Resource = ({ dmmf, prisma }) => ({ 4 | resource: { 5 | model: dmmf.modelMap.PostTag, 6 | client: prisma, 7 | }, 8 | options: {}, 9 | }); 10 | -------------------------------------------------------------------------------- /apps/server/src/language/args/language-order.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field } from '@nestjs/graphql'; 2 | import { LanguageOrder } from '../enums/language-order.enum'; 3 | 4 | @ArgsType() 5 | export class LanguageOrderArgs { 6 | @Field(() => LanguageOrder) 7 | order?: LanguageOrder; 8 | } 9 | -------------------------------------------------------------------------------- /apps/server/src/models/user.model.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '@nestjs/graphql'; 2 | import { Node } from './node.model'; 3 | 4 | @ObjectType({ implements: [Node] }) 5 | export class User { 6 | username: string; 7 | name?: string; 8 | bio?: string; 9 | avatar?: string; 10 | } 11 | -------------------------------------------------------------------------------- /apps/web/graphql/queries/get-searched-repositories.graphql: -------------------------------------------------------------------------------- 1 | query GetSearchedRepositories($first: Int = 5, $searchTerm: String) { 2 | repositories(first: $first, searchTerm: $searchTerm) { 3 | edges { 4 | node { 5 | ...repoPreview 6 | } 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/server/src/models/connections/collect.connection.ts: -------------------------------------------------------------------------------- 1 | import { Paginated } from '@exonest/graphql-connections'; 2 | import { ObjectType } from '@nestjs/graphql'; 3 | import { Collect } from '../collect.model'; 4 | 5 | @ObjectType() 6 | export class CollectConnection extends Paginated(Collect) {} 7 | -------------------------------------------------------------------------------- /apps/server/src/models/connections/license.connection.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '@nestjs/graphql'; 2 | import { Paginated } from '@exonest/graphql-connections'; 3 | import { License } from '../license.model'; 4 | 5 | @ObjectType() 6 | export class LicenseConnection extends Paginated(License) {} 7 | -------------------------------------------------------------------------------- /apps/web-e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "sourceMap": false, 5 | "outDir": "../../dist/out-tsc", 6 | "allowJs": true, 7 | "types": ["cypress", "node"] 8 | }, 9 | "include": ["src/**/*.ts", "src/**/*.js"] 10 | } 11 | -------------------------------------------------------------------------------- /prisma/migrations/20220129100115_add_owner_fields/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Owner" ADD COLUMN "company" TEXT, 3 | ADD COLUMN "contributionsCount" INTEGER, 4 | ADD COLUMN "followersCount" INTEGER, 5 | ADD COLUMN "location" TEXT, 6 | ADD COLUMN "websiteUrl" TEXT; 7 | -------------------------------------------------------------------------------- /apps/server/src/models/connections/language.connection.ts: -------------------------------------------------------------------------------- 1 | import { Paginated } from '@exonest/graphql-connections'; 2 | import { ObjectType } from '@nestjs/graphql'; 3 | import { Language } from '../language.model'; 4 | 5 | @ObjectType() 6 | export class LanguageConnection extends Paginated(Language) {} 7 | -------------------------------------------------------------------------------- /apps/server/src/models/collection.model.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '@nestjs/graphql'; 2 | import { Node } from './node.model'; 3 | 4 | @ObjectType({ implements: [Node] }) 5 | export class Collection { 6 | name: string; 7 | slug: string; 8 | description?: string; 9 | image?: string; 10 | } 11 | -------------------------------------------------------------------------------- /apps/server/src/models/connections/repository.connection.ts: -------------------------------------------------------------------------------- 1 | import { Paginated } from '@exonest/graphql-connections'; 2 | import { ObjectType } from '@nestjs/graphql'; 3 | import { Repository } from '../repository.model'; 4 | 5 | @ObjectType() 6 | export class RepositoryConnection extends Paginated(Repository) {} 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /apps/image-generator/src/post/post.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { PostController } from './post.controller'; 3 | import { PostService } from './post.service'; 4 | 5 | @Module({ 6 | providers: [PostService], 7 | controllers: [PostController], 8 | }) 9 | export class PostModule {} 10 | -------------------------------------------------------------------------------- /apps/server/src/owner/args/platform.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field } from '@nestjs/graphql'; 2 | import { PlatformType } from '../../models/enums/platform-type.enum'; 3 | 4 | @ArgsType() 5 | export class PlatformArgs { 6 | owner: string; 7 | 8 | @Field(() => PlatformType) 9 | platform: PlatformType; 10 | } 11 | -------------------------------------------------------------------------------- /libs/common/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "../../dist/out-tsc", 6 | "declaration": true, 7 | "types": ["node"] 8 | }, 9 | "exclude": ["**/*.spec.ts", "**/*.test.ts"], 10 | "include": ["**/*.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /tools/tsconfig.tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "../dist/out-tsc/tools", 5 | "rootDir": ".", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": ["node"], 9 | "importHelpers": false 10 | }, 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /apps/server/src/metadata/metadata.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { CacheModule } from '../cache/cache.module'; 3 | import { MetadataResolver } from './metadata.resolver'; 4 | 5 | @Module({ 6 | imports: [CacheModule], 7 | providers: [MetadataResolver], 8 | }) 9 | export class MetadataModule {} 10 | -------------------------------------------------------------------------------- /apps/server/src/plugins/pagination-complexity.ts: -------------------------------------------------------------------------------- 1 | import { ComplexityEstimatorArgs } from '@nestjs/graphql'; 2 | 3 | export const paginationComplexity = ({ 4 | args: { first, last }, 5 | }: ComplexityEstimatorArgs) => { 6 | if (!first && !last) return 1000; 7 | if (first) return first; 8 | if (last) return last; 9 | }; 10 | -------------------------------------------------------------------------------- /apps/web/graphql/mutations/send-submission.graphql: -------------------------------------------------------------------------------- 1 | mutation SendSubmission ($username: String!, $platform: PlatformType!) { 2 | sendSubmission(username: $username, platform: $platform) { 3 | submission { 4 | id 5 | } 6 | userErrors { 7 | message 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /codegen.yml: -------------------------------------------------------------------------------- 1 | schema: http://localhost:3333/graphql 2 | documents: apps/web/graphql/**/*.graphql 3 | generates: 4 | apps/web/lib/graphql-types.ts: 5 | plugins: 6 | - typescript 7 | - typescript-operations 8 | - typescript-react-apollo 9 | - fragment-matcher 10 | config: 11 | withHooks: true 12 | -------------------------------------------------------------------------------- /apps/server/src/admin/resources/collect.resource.ts: -------------------------------------------------------------------------------- 1 | import { Resource } from './resource-type'; 2 | 3 | export const collectResource: Resource = ({ dmmf, prisma }) => ({ 4 | resource: { 5 | model: dmmf.modelMap.Collect, 6 | client: prisma, 7 | }, 8 | options: { 9 | // TODO: Blacklisting action 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /apps/server/src/models/post.model.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '@nestjs/graphql'; 2 | import { Node } from './node.model'; 3 | 4 | @ObjectType({ implements: [Node] }) 5 | export class Post { 6 | title: string; 7 | slug: string; 8 | content: string; 9 | contentHtml: string; 10 | summary?: string; 11 | image?: string; 12 | } 13 | -------------------------------------------------------------------------------- /prisma/migrations/20220103122333_added_trend_indicators/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Repository" ADD COLUMN "monthlyTrendIndicator" DOUBLE PRECISION NOT NULL DEFAULT 1, 3 | ADD COLUMN "weeklyTrendIndicator" DOUBLE PRECISION NOT NULL DEFAULT 1, 4 | ADD COLUMN "yearlyTrendIndicator" DOUBLE PRECISION NOT NULL DEFAULT 1; 5 | -------------------------------------------------------------------------------- /apps/server/src/models/args/platform-by-id.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field, ID } from '@nestjs/graphql'; 2 | import { PlatformType } from '../enums/platform-type.enum'; 3 | 4 | @ArgsType() 5 | export class PlatformByIdArgs { 6 | @Field(() => ID) 7 | id: string; 8 | 9 | @Field(() => PlatformType) 10 | platform: PlatformType; 11 | } 12 | -------------------------------------------------------------------------------- /apps/server/src/models/connections/collection.connection.ts: -------------------------------------------------------------------------------- 1 | import { Paginated } from '@exonest/graphql-connections'; 2 | import { ObjectType } from '@nestjs/graphql'; 3 | import { Collection } from '../collection.model'; 4 | 5 | @ObjectType({ description: "Nice name, isn't it?" }) 6 | export class CollectionConnection extends Paginated(Collection) {} 7 | -------------------------------------------------------------------------------- /apps/server/src/repository/args/platform.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field } from '@nestjs/graphql'; 2 | import { PlatformType } from '../../models/enums/platform-type.enum'; 3 | 4 | @ArgsType() 5 | export class PlatformArgs { 6 | repo: string; 7 | owner: string; 8 | 9 | @Field(() => PlatformType) 10 | platform: PlatformType; 11 | } 12 | -------------------------------------------------------------------------------- /libs/telegraf/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "../../dist/out-tsc", 6 | "declaration": true, 7 | "types": ["node"], 8 | "target": "es6" 9 | }, 10 | "exclude": ["**/*.spec.ts", "**/*.test.ts"], 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /apps/web/specs/index.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import Index from '../pages/index'; 5 | 6 | describe('Index', () => { 7 | it('should render successfully', () => { 8 | const { baseElement } = render(); 9 | expect(baseElement).toBeTruthy(); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /libs/monomedia/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "../../dist/out-tsc", 6 | "declaration": true, 7 | "types": ["node"], 8 | "target": "es6" 9 | }, 10 | "exclude": ["**/*.spec.ts", "**/*.test.ts"], 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /apps/server/src/repository/repository.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MarkdownModule } from '../markdown/markdown.module'; 3 | import { RepositoryResolver } from './repository.resolver'; 4 | 5 | @Module({ 6 | imports: [MarkdownModule], 7 | providers: [RepositoryResolver], 8 | }) 9 | export class RepositoryModule {} 10 | -------------------------------------------------------------------------------- /apps/github-bot/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["node"], 7 | "emitDecoratorMetadata": true, 8 | "target": "es2015" 9 | }, 10 | "exclude": ["**/*.spec.ts", "**/*.test.ts"], 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /apps/server/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["node"], 7 | "emitDecoratorMetadata": true, 8 | "target": "es2015" 9 | }, 10 | "exclude": ["**/*.spec.ts", "**/*.test.ts"], 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /apps/github-bot/src/app/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get('') 9 | async follow() { 10 | await this.appService.followOwners(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /apps/image-generator/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["node"], 7 | "emitDecoratorMetadata": true, 8 | "target": "es2015" 9 | }, 10 | "exclude": ["**/*.spec.ts", "**/*.test.ts"], 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /apps/web/graphql/fragments/post-preview.fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment postPreview on Post { 2 | id 3 | slug 4 | title 5 | image 6 | tags { 7 | name 8 | } 9 | summaryLimited 10 | publishedAt { 11 | formatted 12 | difference 13 | } 14 | author { 15 | id 16 | name 17 | username 18 | bio 19 | avatar 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /workspace.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "projects": { 4 | "common": "libs/common", 5 | "github-bot": "apps/github-bot", 6 | "image-generator": "apps/image-generator", 7 | "monomedia": "libs/monomedia", 8 | "server": "apps/server", 9 | "telegraf": "libs/telegraf", 10 | "web": "apps/web", 11 | "web-e2e": "apps/web-e2e" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/server/src/models/submission.model.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from '@nestjs/graphql'; 2 | import { PlatformType } from './enums/platform-type.enum'; 3 | import { Node } from './node.model'; 4 | 5 | @ObjectType({ implements: [Node] }) 6 | export class Submission { 7 | username: string; 8 | 9 | @Field(() => PlatformType) 10 | platform: PlatformType; 11 | } 12 | -------------------------------------------------------------------------------- /apps/server/src/models/report.model.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from '@nestjs/graphql'; 2 | import { ReportableType } from './enums/reportable-type.enum'; 3 | import { Node } from './node.model'; 4 | 5 | @ObjectType({ implements: [Node] }) 6 | export class Report { 7 | reason: string; 8 | 9 | @Field(() => ReportableType) 10 | reportableType: ReportableType; 11 | } 12 | -------------------------------------------------------------------------------- /libs/common/src/lib/slugify/slugify.ts: -------------------------------------------------------------------------------- 1 | import slugify from 'slugify'; 2 | 3 | slugify.extend({ 4 | '#': 'sharp', 5 | '+': 'plus', 6 | '.': 'dot', 7 | '*': 'star', 8 | }); 9 | 10 | const languageMap = { 11 | 'c--': 'cminusminus', 12 | }; 13 | 14 | export const slugifyLanguage = (string) => 15 | languageMap[string.toLowerCase()] || slugify(string.toLowerCase()); 16 | -------------------------------------------------------------------------------- /prisma/migrations/20220127155935_connect_user_and_owners/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Owner" ADD COLUMN "userId" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "User" ALTER COLUMN "name" DROP NOT NULL; 6 | 7 | -- AddForeignKey 8 | ALTER TABLE "Owner" ADD CONSTRAINT "Owner_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; 9 | -------------------------------------------------------------------------------- /apps/server/src/submission/submission.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { GithubDiscovererModule } from '../github-discoverer/github-discoverer.module'; 3 | import { SubmissionResolver } from './submission.resolver'; 4 | 5 | @Module({ 6 | imports: [GithubDiscovererModule], 7 | providers: [SubmissionResolver], 8 | }) 9 | export class SubmissionModule {} 10 | -------------------------------------------------------------------------------- /apps/image-generator/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { PostModule } from '../post/post.module'; 3 | import { AppController } from './app.controller'; 4 | import { AppService } from './app.service'; 5 | 6 | @Module({ 7 | imports: [PostModule], 8 | controllers: [AppController], 9 | providers: [AppService], 10 | }) 11 | export class AppModule {} 12 | -------------------------------------------------------------------------------- /apps/server/src/submission/submission.payload.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from '@nestjs/graphql'; 2 | import { UserError } from '../models/errors/user-error.model'; 3 | import { Submission } from '../models/submission.model'; 4 | 5 | @ObjectType() 6 | export class SubmissionPayload { 7 | submission?: Submission; 8 | 9 | @Field(() => [UserError]) 10 | userErrors?: UserError[]; 11 | } 12 | -------------------------------------------------------------------------------- /apps/web/components/SkeletonLoader/SkeletonLoaderShape.module.css: -------------------------------------------------------------------------------- 1 | .shimmer { 2 | color: grey; 3 | display: inline-block; 4 | -webkit-mask: linear-gradient(-60deg, #000 30%, #0005, #000 70%) right/500% 5 | 100%; 6 | background-repeat: no-repeat; 7 | animation: shimmer 2.5s infinite; 8 | } 9 | 10 | @keyframes shimmer { 11 | 100% { 12 | -webkit-mask-position: left; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/web/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'web', 3 | preset: '../../jest.preset.js', 4 | transform: { 5 | '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest', 6 | '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/next/babel'] }], 7 | }, 8 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 9 | coverageDirectory: '../../coverage/apps/web', 10 | }; 11 | -------------------------------------------------------------------------------- /apps/web/components/UI/Button/GhostButton.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import { Button, ButtonProps } from './Button'; 3 | 4 | export const GhostButton = ({ 5 | children, 6 | className, 7 | ...props 8 | }: ButtonProps) => { 9 | return ( 10 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /libs/common/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/common/src/lib/humanly-readable-date/humanly-readable-date.spec.ts: -------------------------------------------------------------------------------- 1 | import { humanlyReadableDate } from './humanly-readable-date'; 2 | 3 | describe('humanlyReadableDate', () => { 4 | it('should work', () => { 5 | expect( 6 | humanlyReadableDate( 7 | new Date('Thu Nov 25 2021 15:17:08 GMT+0330 (Iran Standard Time)') 8 | ) 9 | ).toEqual('4 آذر 1400'); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /apps/github-bot/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /apps/web/graphql/queries/get-tag.graphql: -------------------------------------------------------------------------------- 1 | query GetTag($name: String!, $postsCount: Int = 12, $postsAfter: String) { 2 | tag(name: $name) { 3 | name 4 | posts(first: $postsCount, after: $postsAfter) { 5 | edges { 6 | node { 7 | ...postPreview 8 | } 9 | } 10 | pageInfo { 11 | hasNextPage 12 | endCursor 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /libs/common/src/lib/persian-numbers/persian-numbers.spec.ts: -------------------------------------------------------------------------------- 1 | import { persianNumbers } from './persian-numbers'; 2 | 3 | describe('persianNumbers', () => { 4 | it('should work', () => { 5 | expect(persianNumbers('87')).toEqual('۸۷'); 6 | }); 7 | 8 | it('should work with strings with numbers', () => { 9 | expect(persianNumbers("The bite of '87")).toEqual("The bite of '۸۷"); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /libs/monomedia/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/telegraf/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /apps/image-generator/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/common/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/guess-direction'; 2 | export * from './lib/humanly-readable-date'; 3 | export * from './lib/links'; 4 | export * from './lib/null-to-undefined'; 5 | export * from './lib/persian-numbers'; 6 | export * from './lib/random'; 7 | export * from './lib/repo-requirements'; 8 | export * from './lib/slugify'; 9 | export * from './lib/timer'; 10 | export * from './lib/word-limiter'; 11 | -------------------------------------------------------------------------------- /apps/server/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'server', 3 | preset: '../../jest.preset.js', 4 | globals: { 5 | 'ts-jest': { 6 | tsconfig: '/tsconfig.spec.json', 7 | }, 8 | }, 9 | testEnvironment: 'node', 10 | transform: { 11 | '^.+\\.[tj]s$': 'ts-jest', 12 | }, 13 | moduleFileExtensions: ['ts', 'js', 'html'], 14 | coverageDirectory: '../../coverage/apps/server', 15 | }; 16 | -------------------------------------------------------------------------------- /apps/server/src/models/rgba.model.ts: -------------------------------------------------------------------------------- 1 | import { CacheControl } from '@exonest/graphql-cache-control'; 2 | import { Field, Int, ObjectType } from '@nestjs/graphql'; 3 | 4 | @CacheControl({ inheritMaxAge: true }) 5 | @ObjectType() 6 | export class RGBA { 7 | @Field(() => Int) 8 | red: number; 9 | 10 | @Field(() => Int) 11 | green: number; 12 | 13 | @Field(() => Int) 14 | blue: number; 15 | 16 | alpha: number; 17 | } 18 | -------------------------------------------------------------------------------- /apps/web/components/UI/Divider.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | 3 | export interface DividerProps { 4 | className?: string; 5 | } 6 | 7 | export const Divider = ({ className }: DividerProps) => { 8 | return ( 9 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /libs/common/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'common', 3 | preset: '../../jest.preset.js', 4 | globals: { 5 | 'ts-jest': { 6 | tsconfig: '/tsconfig.spec.json', 7 | }, 8 | }, 9 | testEnvironment: 'node', 10 | transform: { 11 | '^.+\\.[tj]sx?$': 'ts-jest', 12 | }, 13 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 14 | coverageDirectory: '../../coverage/libs/common', 15 | }; 16 | -------------------------------------------------------------------------------- /apps/github-bot/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'github-bot', 3 | preset: '../../jest.preset.js', 4 | globals: { 5 | 'ts-jest': { 6 | tsconfig: '/tsconfig.spec.json', 7 | }, 8 | }, 9 | testEnvironment: 'node', 10 | transform: { 11 | '^.+\\.[tj]s$': 'ts-jest', 12 | }, 13 | moduleFileExtensions: ['ts', 'js', 'html'], 14 | coverageDirectory: '../../coverage/apps/github-bot', 15 | }; 16 | -------------------------------------------------------------------------------- /apps/web/graphql/queries/get-post.graphql: -------------------------------------------------------------------------------- 1 | query GetPost($slug: String!) { 2 | postBySlug(slug: $slug) { 3 | id 4 | slug 5 | title 6 | image 7 | contentHtml 8 | tags { 9 | name 10 | } 11 | summaryLimited 12 | publishedAt { 13 | formatted 14 | difference 15 | } 16 | author { 17 | id 18 | name 19 | username 20 | bio 21 | avatar 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /libs/telegraf/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'telegraf', 3 | preset: '../../jest.preset.js', 4 | globals: { 5 | 'ts-jest': { 6 | tsconfig: '/tsconfig.spec.json', 7 | }, 8 | }, 9 | testEnvironment: 'node', 10 | transform: { 11 | '^.+\\.[tj]sx?$': 'ts-jest', 12 | }, 13 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 14 | coverageDirectory: '../../coverage/libs/telegraf', 15 | }; 16 | -------------------------------------------------------------------------------- /libs/monomedia/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'monomedia', 3 | preset: '../../jest.preset.js', 4 | globals: { 5 | 'ts-jest': { 6 | tsconfig: '/tsconfig.spec.json', 7 | }, 8 | }, 9 | testEnvironment: 'node', 10 | transform: { 11 | '^.+\\.[tj]sx?$': 'ts-jest', 12 | }, 13 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 14 | coverageDirectory: '../../coverage/libs/monomedia', 15 | }; 16 | -------------------------------------------------------------------------------- /apps/web/components/UI/Button/OutlineButton.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import { Button, ButtonProps } from './Button'; 3 | 4 | export const OutlineButton = ({ 5 | children, 6 | className, 7 | ...props 8 | }: ButtonProps) => { 9 | return ( 10 | 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /apps/image-generator/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'image-generator', 3 | preset: '../../jest.preset.js', 4 | globals: { 5 | 'ts-jest': { 6 | tsconfig: '/tsconfig.spec.json', 7 | }, 8 | }, 9 | testEnvironment: 'node', 10 | transform: { 11 | '^.+\\.[tj]s$': 'ts-jest', 12 | }, 13 | moduleFileExtensions: ['ts', 'js', 'html'], 14 | coverageDirectory: '../../coverage/apps/image-generator', 15 | }; 16 | -------------------------------------------------------------------------------- /apps/web-e2e/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["src/plugins/index.js"], 11 | "rules": { 12 | "@typescript-eslint/no-var-requires": "off", 13 | "no-undef": "off" 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /apps/web-e2e/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileServerFolder": ".", 3 | "fixturesFolder": "./src/fixtures", 4 | "integrationFolder": "./src/integration", 5 | "modifyObstructiveCode": false, 6 | "supportFile": "./src/support/index.ts", 7 | "pluginsFile": false, 8 | "video": true, 9 | "videosFolder": "../../dist/cypress/apps/web-e2e/videos", 10 | "screenshotsFolder": "../../dist/cypress/apps/web-e2e/screenshots", 11 | "chromeWebSecurity": false 12 | } 13 | -------------------------------------------------------------------------------- /libs/common/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "**/*.test.ts", 10 | "**/*.spec.ts", 11 | "**/*.test.tsx", 12 | "**/*.spec.tsx", 13 | "**/*.test.js", 14 | "**/*.spec.js", 15 | "**/*.test.jsx", 16 | "**/*.spec.jsx", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /libs/telegraf/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "**/*.test.ts", 10 | "**/*.spec.ts", 11 | "**/*.test.tsx", 12 | "**/*.spec.tsx", 13 | "**/*.test.js", 14 | "**/*.spec.js", 15 | "**/*.test.jsx", 16 | "**/*.spec.jsx", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /libs/monomedia/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "**/*.test.ts", 10 | "**/*.spec.ts", 11 | "**/*.test.tsx", 12 | "**/*.spec.tsx", 13 | "**/*.test.js", 14 | "**/*.spec.js", 15 | "**/*.test.jsx", 16 | "**/*.spec.jsx", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /apps/server/src/post/post.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MarkdownModule } from '../markdown/markdown.module'; 3 | import { PostProcessor } from './post.processor'; 4 | import { PostResolver } from './post.resolver'; 5 | import { PostService } from './post.service'; 6 | 7 | @Module({ 8 | imports: [MarkdownModule], 9 | providers: [PostService, PostResolver, PostProcessor], 10 | exports: [PostService], 11 | }) 12 | export class PostModule {} 13 | -------------------------------------------------------------------------------- /apps/web/graphql/queries/get-collections.graphql: -------------------------------------------------------------------------------- 1 | query GetCollections($count: Int, $after: String) { 2 | collections(first: $count, after: $after) { 3 | pageInfo { 4 | hasNextPage 5 | endCursor 6 | } 7 | edges { 8 | node { 9 | id 10 | name 11 | slug 12 | description 13 | repositoriesCount 14 | image 15 | color { 16 | hexString 17 | } 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/web/next-sitemap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('next-sitemap').IConfig} 3 | */ 4 | module.exports = { 5 | siteUrl: process.env.SITE_URL || 'https://matnbaz.net/', 6 | sourceDir: 'dist/apps/web/.next', 7 | outDir: 'apps/web/public', 8 | generateRobotsTxt: true, 9 | exclude: ['*/404', '*/500', '*/_middleware'], 10 | robotsTxtOptions: { 11 | additionalSitemaps: [ 12 | 'https://matnbaz.net/server-sitemap.xml' 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/web/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"], 7 | "jsx": "react" 8 | }, 9 | "include": [ 10 | "**/*.test.ts", 11 | "**/*.spec.ts", 12 | "**/*.test.tsx", 13 | "**/*.spec.tsx", 14 | "**/*.test.js", 15 | "**/*.spec.js", 16 | "**/*.test.jsx", 17 | "**/*.spec.jsx", 18 | "**/*.d.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /apps/server/src/admin/resources/resource-type.ts: -------------------------------------------------------------------------------- 1 | import { DMMFClass } from '@prisma/client/runtime'; 2 | import { ResourceWithOptions } from 'adminjs'; 3 | import { Queue } from 'bull'; 4 | import { PrismaService } from 'nestjs-prisma'; 5 | export interface ResourceContext { 6 | dmmf: DMMFClass; 7 | prisma: PrismaService; 8 | queues: { 9 | github: Queue; 10 | main: Queue; 11 | }; 12 | } 13 | 14 | export type Resource = (context: ResourceContext) => ResourceWithOptions; 15 | -------------------------------------------------------------------------------- /apps/server/src/cache/cache.module.ts: -------------------------------------------------------------------------------- 1 | import { CacheModule as NestCacheModule, Module } from '@nestjs/common'; 2 | import * as redisStore from 'cache-manager-redis-store'; 3 | 4 | @Module({ 5 | imports: [ 6 | NestCacheModule.register({ 7 | isGlobal: true, 8 | store: redisStore, 9 | // Store-specific configuration: 10 | host: 'localhost', 11 | port: 6379, 12 | }), 13 | ], 14 | exports: [NestCacheModule], 15 | }) 16 | export class CacheModule {} 17 | -------------------------------------------------------------------------------- /apps/server/src/topic/enums/topics-order.enum.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from '@nestjs/graphql'; 2 | 3 | export enum TopicOrder { 4 | REPOSITORIES_DESC = 'REPOSITORIES_DESC', 5 | } 6 | 7 | registerEnumType(TopicOrder, { 8 | name: 'TopicOrder', 9 | description: 'You can order repositories with one of these options.', 10 | valuesMap: { 11 | REPOSITORIES_DESC: { 12 | description: 'Order by repositories count in descending direction.', 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /apps/web-e2e/src/integration/app.spec.ts: -------------------------------------------------------------------------------- 1 | import { getGreeting } from '../support/app.po'; 2 | 3 | describe('web', () => { 4 | beforeEach(() => cy.visit('/')); 5 | 6 | it('should display welcome message', () => { 7 | // Custom command example, see `../support/commands.ts` file 8 | cy.login('my-email@something.com', 'myPassword'); 9 | 10 | // Function helper example, see `../support/app.po.ts` file 11 | getGreeting().contains('Welcome to web!'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /prisma/migrations/20220129134338_add_public_contributions/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - Added the required column `publicContributionsCount` to the `OwnerStatistic` table without a default value. This is not possible if the table is not empty. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "Owner" ADD COLUMN "publicContributionsCount" INTEGER; 9 | 10 | -- AlterTable 11 | ALTER TABLE "OwnerStatistic" ADD COLUMN "publicContributionsCount" INTEGER NOT NULL DEFAULT(0); 12 | -------------------------------------------------------------------------------- /apps/server/src/license/enums/license-order.enum.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from '@nestjs/graphql'; 2 | 3 | export enum LicenseOrder { 4 | REPOSITORIES_DESC = 'REPOSITORIES_DESC', 5 | } 6 | 7 | registerEnumType(LicenseOrder, { 8 | name: 'LicenseOrder', 9 | description: 'You can order repositories with one of these options.', 10 | valuesMap: { 11 | REPOSITORIES_DESC: { 12 | description: 'Order by repositories count in descending direction.', 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /apps/server/src/language/enums/language-order.enum.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from '@nestjs/graphql'; 2 | 3 | export enum LanguageOrder { 4 | REPOSITORIES_DESC = 'REPOSITORIES_DESC', 5 | } 6 | 7 | registerEnumType(LanguageOrder, { 8 | name: 'LanguageOrder', 9 | description: 'You can order repositories with one of these options.', 10 | valuesMap: { 11 | REPOSITORIES_DESC: { 12 | description: 'Order by repositories count in descending direction.', 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /apps/server/src/models/enums/script-direction.enum.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from '@nestjs/graphql'; 2 | 3 | export enum ScriptDirection { 4 | RTL = 'RTL', 5 | LTR = 'LTR', 6 | } 7 | 8 | registerEnumType(ScriptDirection, { 9 | name: 'ScriptDirection', 10 | description: 'A repository owner could any of these types.', 11 | valuesMap: { 12 | RTL: { 13 | description: 'right-to-left', 14 | }, 15 | LTR: { 16 | description: 'left-to-right', 17 | }, 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /apps/server/src/models/enums/owner-type.enum.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from '@nestjs/graphql'; 2 | 3 | export enum OwnerType { 4 | User = 'User', 5 | Organization = 'Organization', 6 | } 7 | 8 | registerEnumType(OwnerType, { 9 | name: 'OwnerType', 10 | description: 'A repository owner could any of these types.', 11 | valuesMap: { 12 | Organization: { 13 | description: 'Owner is an organization.', 14 | }, 15 | User: { 16 | description: 'Owner is a user.', 17 | }, 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /apps/web/graphql/queries/get-repository.graphql: -------------------------------------------------------------------------------- 1 | query GetRepository( 2 | $owner: String! 3 | $repo: String! 4 | $platform: PlatformType! 5 | $relatedReposFirst: Int = 8 6 | ) { 7 | repositoryByPlatform(owner: $owner, repo: $repo, platform: $platform) { 8 | ...repoFull 9 | relatedRepos(first: $relatedReposFirst) { 10 | edges { 11 | node { 12 | ...repoPreview 13 | } 14 | } 15 | pageInfo { 16 | hasNextPage 17 | endCursor 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/web/graphql/fragments/repo-preview.fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment repoPreviewWithoutOwner on Repository { 2 | id 3 | fullName 4 | platformUrl 5 | platform 6 | descriptionLimited 7 | descriptionDirection 8 | stargazersCount 9 | forksCount 10 | openIssuesCount 11 | language { 12 | name 13 | color { 14 | hexString 15 | } 16 | } 17 | isNew 18 | } 19 | 20 | fragment repoPreview on Repository { 21 | ...repoPreviewWithoutOwner 22 | owner { 23 | type 24 | login 25 | platformId 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/github-bot/src/main.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@nestjs/common'; 2 | import { NestFactory } from '@nestjs/core'; 3 | import { AppModule } from './app/app.module'; 4 | 5 | async function bootstrap() { 6 | const app = await NestFactory.create(AppModule); 7 | const globalPrefix = ''; 8 | app.setGlobalPrefix(globalPrefix); 9 | const port = process.env.GITHUB_BOT_PORT || 3334; 10 | await app.listen(port, () => { 11 | Logger.log('Listening at http://localhost:' + port + '/' + globalPrefix); 12 | }); 13 | } 14 | 15 | bootstrap(); 16 | -------------------------------------------------------------------------------- /apps/server/src/models/enums/reportable-type.enum.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from '@nestjs/graphql'; 2 | 3 | export enum ReportableType { 4 | Owner = 'Owner', 5 | Repository = 'Repository', 6 | } 7 | 8 | registerEnumType(ReportableType, { 9 | name: 'ReportableType', 10 | description: 'A reportable could any of these types.', 11 | valuesMap: { 12 | Owner: { 13 | description: 'Reportable is an owner.', 14 | }, 15 | Repository: { 16 | description: 'Reportable is a repository.', 17 | }, 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /libs/monomedia/src/lib/monomedia.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test } from '@nestjs/testing'; 2 | import { MonomediaService } from './monomedia.service'; 3 | 4 | describe('MonomediaService', () => { 5 | let service: MonomediaService; 6 | 7 | beforeEach(async () => { 8 | const module = await Test.createTestingModule({ 9 | providers: [MonomediaService], 10 | }).compile(); 11 | 12 | service = module.get(MonomediaService); 13 | }); 14 | 15 | it('should be defined', () => { 16 | expect(service).toBeTruthy(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /apps/server/src/github-discoverer/repo-discovery-terms.ts: -------------------------------------------------------------------------------- 1 | export const repoDiscoveryTerms = [ 2 | 'iranian', 3 | 'topic:iranian', 4 | 'persian', 5 | 'topic:persian', 6 | 'iran', 7 | 'topic:iran', 8 | 'farsi', 9 | 'topic:farsi', 10 | 'فارسی', 11 | 'پارسی', 12 | 'ایران', 13 | 'ایرانیان', 14 | 'پرداخت', 15 | 'دانشگاه', 16 | 'بسته', 17 | 'کتاب‌خانه', 18 | 'کتابخانه', 19 | 'کتاب خانه', 20 | 'پکیج', 21 | 'قالب', 22 | 'نرم‌افزار', 23 | 'نرم افزار', 24 | 'راست چین', 25 | 'راست‌چین', 26 | 'آیکون', 27 | 'آیکن', 28 | ]; 29 | -------------------------------------------------------------------------------- /apps/web/components/UI/Button/RedButton.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import { Button, ButtonProps } from './Button'; 3 | 4 | export const RedButton = ({ children, className, ...props }: ButtonProps) => { 5 | return ( 6 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /libs/common/src/lib/null-to-undefined/null-to-undefined.spec.ts: -------------------------------------------------------------------------------- 1 | import { nullToUndefined } from './null-to-undefined'; 2 | 3 | describe('nullToUndefined', () => { 4 | it('should work', () => { 5 | expect(nullToUndefined('non null value')).toEqual('non null value'); 6 | expect(nullToUndefined(null)).toEqual(undefined); 7 | expect(nullToUndefined(1, (value) => ({ target: value }))).toEqual({ 8 | target: 1, 9 | }); 10 | expect(nullToUndefined(null, (value) => ({ target: value }))).toEqual( 11 | undefined 12 | ); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /apps/server/src/color/color.resolver.ts: -------------------------------------------------------------------------------- 1 | import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; 2 | import { Color } from '../models/color.model'; 3 | import { RGBA } from '../models/rgba.model'; 4 | import { colorHexToDecimal } from './utils'; 5 | 6 | @Resolver(() => Color) 7 | export class ColorResolver { 8 | @ResolveField(() => RGBA) 9 | rgba(@Parent() { hexString }: Color) { 10 | const rgb = colorHexToDecimal(hexString); 11 | return { 12 | red: rgb[0], 13 | green: rgb[1], 14 | blue: rgb[2], 15 | alpha: 1, 16 | }; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "jsx": "preserve", 5 | "allowJs": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "types": ["node", "jest"], 9 | "strict": false, 10 | "forceConsistentCasingInFileNames": true, 11 | "noEmit": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "incremental": true 15 | }, 16 | "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"], 17 | "exclude": ["node_modules"] 18 | } 19 | -------------------------------------------------------------------------------- /prisma/migrations/20220109114338_add_owner_statistic/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "OwnerStatistic" ( 3 | "id" TEXT NOT NULL, 4 | "contributionsCount" INTEGER NOT NULL, 5 | "followersCount" INTEGER NOT NULL, 6 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 7 | "ownerId" TEXT NOT NULL, 8 | 9 | CONSTRAINT "OwnerStatistic_pkey" PRIMARY KEY ("id") 10 | ); 11 | 12 | -- AddForeignKey 13 | ALTER TABLE "OwnerStatistic" ADD CONSTRAINT "OwnerStatistic_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "Owner"("id") ON DELETE CASCADE ON UPDATE CASCADE; 14 | -------------------------------------------------------------------------------- /apps/server/src/hybrid-throttler.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, ExecutionContext } from '@nestjs/common'; 2 | import { GqlExecutionContext } from '@nestjs/graphql'; 3 | import { ThrottlerGuard } from '@nestjs/throttler'; 4 | 5 | @Injectable() 6 | export class HybridThrottlerGuard extends ThrottlerGuard { 7 | getRequestResponse(context: ExecutionContext) { 8 | if (context.getType() === 'http') { 9 | return super.getRequestResponse(context); 10 | } 11 | const gqlCtx = GqlExecutionContext.create(context); 12 | const { req, res } = gqlCtx.getContext(); 13 | return { req, res }; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/web/components/UI/Button/PrimaryButton.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import { Button, ButtonProps } from './Button'; 3 | 4 | export const PrimaryButton = ({ 5 | children, 6 | className, 7 | ...props 8 | }: ButtonProps) => { 9 | return ( 10 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /libs/common/src/lib/guess-direction/guess-direction.spec.ts: -------------------------------------------------------------------------------- 1 | import { guessDirection } from './guess-direction'; 2 | 3 | describe('guessDirection', () => { 4 | it('should work', () => { 5 | expect( 6 | guessDirection('متن فارسی که باید راست‌چین تشخیص داده شود.') 7 | ).toEqual('RTL'); 8 | 9 | expect( 10 | guessDirection('English text that should be determined as left-ro-right.') 11 | ).toEqual('LTR'); 12 | 13 | expect( 14 | guessDirection( 15 | 'This one is mixed up (قاطی‌شده), so should be RTL as a default.' 16 | ) 17 | ).toEqual('RTL'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /libs/common/src/lib/timer/timer.ts: -------------------------------------------------------------------------------- 1 | export class Timer { 2 | timerId: number; 3 | start: number; 4 | 5 | constructor(private callback: () => unknown, private remaining: number) { 6 | this.resume(); 7 | } 8 | 9 | pause() { 10 | window.clearTimeout(this.timerId); 11 | this.timerId = null; 12 | this.remaining -= Date.now() - this.start; 13 | } 14 | 15 | resume() { 16 | if (this.timerId) return; 17 | this.start = Date.now(); 18 | this.timerId = window.setTimeout(this.callback, this.remaining); 19 | } 20 | 21 | isPaused() { 22 | return !this.timerId; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/server/src/app/app.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test } from '@nestjs/testing'; 2 | 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppService', () => { 6 | let service: AppService; 7 | 8 | beforeAll(async () => { 9 | const app = await Test.createTestingModule({ 10 | providers: [AppService], 11 | }).compile(); 12 | 13 | service = app.get(AppService); 14 | }); 15 | 16 | describe('getData', () => { 17 | it('should return "Welcome to api!"', () => { 18 | expect(service.getData()).toEqual({ message: 'Welcome to api!' }); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /apps/web/components/SkeletonLoader/LanguagesFilterSkeletonLoader.tsx: -------------------------------------------------------------------------------- 1 | import { SkeletonLoaderShape } from './SkeletonLoaderShape'; 2 | 3 | export const LanguagesFilterSkeletonLoader = () => { 4 | return ( 5 |
6 | 12 | 18 |
19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /libs/common/src/lib/word-limiter/word-limiter.ts: -------------------------------------------------------------------------------- 1 | export const limitWords = (text: string, maxLength = 256) => { 2 | // return the original string if the length is less than max length 3 | if (text.length <= maxLength) return text; 4 | 5 | // trim the string to the maximum length 6 | let trimmedString = text.substr(0, maxLength); 7 | 8 | // re-trim if we are in the middle of a word so it wo-... 9 | trimmedString = trimmedString.substr( 10 | 0, 11 | Math.min(trimmedString.length, trimmedString.lastIndexOf(' ')) 12 | ); 13 | 14 | return trimmedString.length > 0 ? trimmedString + '...' : null; 15 | }; 16 | -------------------------------------------------------------------------------- /apps/github-bot/src/app/app.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test } from '@nestjs/testing'; 2 | 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppService', () => { 6 | let service: AppService; 7 | 8 | beforeAll(async () => { 9 | const app = await Test.createTestingModule({ 10 | providers: [AppService], 11 | }).compile(); 12 | 13 | service = app.get(AppService); 14 | }); 15 | 16 | describe('getData', () => { 17 | it('should return "Welcome to github-bot!"', () => { 18 | expect(service.getData()).toEqual({ message: 'Welcome to github-bot!' }); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /apps/server/src/models/enums/platform-type.enum.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from '@nestjs/graphql'; 2 | 3 | export enum PlatformType { 4 | GitHub = 'GitHub', 5 | GitLab = 'GitLab', 6 | Bitbucket = 'Bitbucket', 7 | } 8 | 9 | registerEnumType(PlatformType, { 10 | name: 'PlatformType', 11 | description: 'A repository owner could any of these types.', 12 | valuesMap: { 13 | GitHub: { 14 | description: 'https://github.com', 15 | }, 16 | GitLab: { 17 | description: 'https://gitlab.com', 18 | }, 19 | Bitbucket: { 20 | description: 'https://bitbucket.com', 21 | }, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /apps/web/graphql/queries/get-owner.graphql: -------------------------------------------------------------------------------- 1 | query GetOwner( 2 | $owner: String! 3 | $platform: PlatformType! 4 | $reposCount: Int = 12 5 | $reposAfter: String 6 | ) { 7 | ownerByPlatform(owner: $owner, platform: $platform) { 8 | repositories(first: $reposCount, after: $reposAfter) { 9 | edges { 10 | cursor 11 | node { 12 | ...repoPreviewWithoutOwner 13 | } 14 | } 15 | pageInfo { 16 | hasNextPage 17 | endCursor 18 | } 19 | } 20 | id 21 | repositoriesCount 22 | type 23 | login 24 | platformId 25 | platform 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/web/graphql/queries/get-collection.graphql: -------------------------------------------------------------------------------- 1 | query GetCollection( 2 | $slug: String! 3 | $reposCount: Int = 12 4 | $reposAfter: String 5 | ) { 6 | collection(slug: $slug) { 7 | id 8 | name 9 | slug 10 | description 11 | repositoriesCount 12 | image 13 | color { 14 | hexString 15 | } 16 | collects(first: $reposCount, after: $reposAfter) { 17 | pageInfo { 18 | hasNextPage 19 | endCursor 20 | } 21 | edges { 22 | node { 23 | repository { 24 | ...repoPreview 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /libs/common/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "libs/common", 3 | "sourceRoot": "libs/common/src", 4 | "projectType": "library", 5 | "targets": { 6 | "lint": { 7 | "executor": "@nrwl/linter:eslint", 8 | "outputs": ["{options.outputFile}"], 9 | "options": { 10 | "lintFilePatterns": ["libs/common/**/*.ts"] 11 | } 12 | }, 13 | "test": { 14 | "executor": "@nrwl/jest:jest", 15 | "outputs": ["coverage/libs/common"], 16 | "options": { 17 | "jestConfig": "libs/common/jest.config.js", 18 | "passWithNoTests": true 19 | } 20 | } 21 | }, 22 | "tags": [] 23 | } 24 | -------------------------------------------------------------------------------- /libs/common/src/lib/persian-numbers/persian-numbers.ts: -------------------------------------------------------------------------------- 1 | export const persianNumbers = ( 2 | string: number | string, 3 | replaceManually = false 4 | ) => { 5 | if (typeof window === 'undefined') string = string.toString(); 6 | if (typeof string === 'number' && !replaceManually) 7 | return string.toLocaleString('fa'); 8 | string = string.toString(); 9 | 10 | const map = { 11 | 1: '۱', 12 | 2: '۲', 13 | 3: '۳', 14 | 4: '۴', 15 | 5: '۵', 16 | 6: '۶', 17 | 7: '۷', 18 | 8: '۸', 19 | 9: '۹', 20 | 0: '۰', 21 | }; 22 | 23 | return string.replace(/[1234567890]/g, (m) => map[m]); 24 | }; 25 | -------------------------------------------------------------------------------- /prisma/migrations/20220127160100_add_username_and_profile_to_user/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - A unique constraint covering the columns `[username]` on the table `User` will be added. If there are existing duplicate values, this will fail. 5 | - Added the required column `username` to the `User` table without a default value. This is not possible if the table is not empty. 6 | 7 | */ 8 | -- AlterTable 9 | ALTER TABLE "User" ADD COLUMN "avatar" TEXT, 10 | ADD COLUMN "bio" TEXT, 11 | ADD COLUMN "username" TEXT NOT NULL; 12 | 13 | -- CreateIndex 14 | CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); 15 | -------------------------------------------------------------------------------- /apps/server/src/owner/args/owner-filter.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field } from '@nestjs/graphql'; 2 | import { OwnerType } from '../../models/enums/owner-type.enum'; 3 | import { PlatformType } from '../../models/enums/platform-type.enum'; 4 | 5 | @ArgsType() 6 | export class OwnerFilterArgs { 7 | /** 8 | * Type of the owner 9 | */ 10 | @Field(() => OwnerType) 11 | type?: OwnerType; 12 | 13 | /** 14 | * Retrieves only the records that have `followersCount` and `contributionsCount` 15 | */ 16 | withStatistics?: boolean; 17 | 18 | /** 19 | * The platform owner belongs to 20 | */ 21 | platform?: PlatformType; 22 | } 23 | -------------------------------------------------------------------------------- /apps/web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:@nrwl/nx/react-typescript", 4 | "../../.eslintrc.json", 5 | "next", 6 | "next/core-web-vitals" 7 | ], 8 | "ignorePatterns": ["!**/*"], 9 | "overrides": [ 10 | { 11 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 12 | "rules": { 13 | "@next/next/no-html-link-for-pages": ["error", "apps/web/pages"] 14 | } 15 | }, 16 | { 17 | "files": ["*.ts", "*.tsx"], 18 | "rules": {} 19 | }, 20 | { 21 | "files": ["*.js", "*.jsx"], 22 | "rules": {} 23 | } 24 | ], 25 | "env": { 26 | "jest": true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /libs/telegraf/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "libs/telegraf", 3 | "sourceRoot": "libs/telegraf/src", 4 | "projectType": "library", 5 | "targets": { 6 | "lint": { 7 | "executor": "@nrwl/linter:eslint", 8 | "outputs": ["{options.outputFile}"], 9 | "options": { 10 | "lintFilePatterns": ["libs/telegraf/**/*.ts"] 11 | } 12 | }, 13 | "test": { 14 | "executor": "@nrwl/jest:jest", 15 | "outputs": ["coverage/libs/telegraf"], 16 | "options": { 17 | "jestConfig": "libs/telegraf/jest.config.js", 18 | "passWithNoTests": true 19 | } 20 | } 21 | }, 22 | "tags": [] 23 | } 24 | -------------------------------------------------------------------------------- /libs/monomedia/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "libs/monomedia", 3 | "sourceRoot": "libs/monomedia/src", 4 | "projectType": "library", 5 | "targets": { 6 | "lint": { 7 | "executor": "@nrwl/linter:eslint", 8 | "outputs": ["{options.outputFile}"], 9 | "options": { 10 | "lintFilePatterns": ["libs/monomedia/**/*.ts"] 11 | } 12 | }, 13 | "test": { 14 | "executor": "@nrwl/jest:jest", 15 | "outputs": ["coverage/libs/monomedia"], 16 | "options": { 17 | "jestConfig": "libs/monomedia/jest.config.js", 18 | "passWithNoTests": true 19 | } 20 | } 21 | }, 22 | "tags": [] 23 | } 24 | -------------------------------------------------------------------------------- /apps/web/graphql/queries/get-posts.graphql: -------------------------------------------------------------------------------- 1 | query GetPosts($count: Int = 12, $after: String) { 2 | posts(first: $count, after: $after) { 3 | pageInfo { 4 | hasNextPage 5 | endCursor 6 | } 7 | edges { 8 | node { 9 | id 10 | slug 11 | title 12 | image 13 | tags { 14 | name 15 | } 16 | summaryLimited 17 | publishedAt { 18 | formatted 19 | difference 20 | } 21 | author { 22 | id 23 | name 24 | username 25 | bio 26 | avatar 27 | } 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /apps/web/graphql/queries/get-github-owners.graphql: -------------------------------------------------------------------------------- 1 | query GetGithubOwners( 2 | $first: Int = 20 3 | $after: String 4 | $order: OwnerOrder = PUBLIC_CONTRIBUTIONS_DESC 5 | ) { 6 | owners( 7 | first: $first 8 | type: User 9 | after: $after 10 | order: $order 11 | withStatistics: true 12 | ) { 13 | pageInfo { 14 | hasNextPage 15 | endCursor 16 | } 17 | edges { 18 | node { 19 | name 20 | login 21 | contributionsCount 22 | publicContributionsCount 23 | followersCount 24 | twitterUsername 25 | websiteUrl 26 | company 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /apps/image-generator/src/app/app.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test } from '@nestjs/testing'; 2 | 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppService', () => { 6 | let service: AppService; 7 | 8 | beforeAll(async () => { 9 | const app = await Test.createTestingModule({ 10 | providers: [AppService], 11 | }).compile(); 12 | 13 | service = app.get(AppService); 14 | }); 15 | 16 | describe('getData', () => { 17 | it('should return "Welcome to image-generator!"', () => { 18 | expect(service.getData()).toEqual({ 19 | message: 'Welcome to image-generator!', 20 | }); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /apps/server/src/repository/enums/fork-status-type.enum.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from '@nestjs/graphql'; 2 | 3 | export enum ForkStatusType { 4 | ALL = 'ALL', 5 | SOURCE = 'SOURCE', 6 | FORK = 'FORK', 7 | } 8 | 9 | registerEnumType(ForkStatusType, { 10 | name: 'ForkStatusType', 11 | description: 'The repo type used in filters.', 12 | valuesMap: { 13 | ALL: { 14 | description: "Doesn't apply any filter to the query.", 15 | }, 16 | FORK: { 17 | description: 'Only returns the forked repositories.', 18 | }, 19 | SOURCE: { 20 | description: 'Only returns the source (not forked) repositories.', 21 | }, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /apps/server/src/main.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@nestjs/common'; 2 | import { NestFactory } from '@nestjs/core'; 3 | import { AppModule } from './app/app.module'; 4 | 5 | async function bootstrap() { 6 | const app = await NestFactory.create(AppModule); 7 | const globalPrefix = ''; 8 | app.setGlobalPrefix(globalPrefix); 9 | const port = process.env.API_PORT || 3333; 10 | app.enableCors({ 11 | origin: true, 12 | methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS', 13 | credentials: true, 14 | }); 15 | await app.listen(port, () => { 16 | Logger.log('Listening at http://localhost:' + port + '/' + globalPrefix); 17 | }); 18 | } 19 | 20 | bootstrap(); 21 | -------------------------------------------------------------------------------- /libs/common/src/lib/slugify/slugify.spec.ts: -------------------------------------------------------------------------------- 1 | import { slugifyLanguage } from './slugify'; 2 | 3 | describe('slugifyLanguage', () => { 4 | it('should work', () => { 5 | expect(slugifyLanguage('objective c')).toEqual('objective-c'); 6 | }); 7 | 8 | it('should work for exceptional languages', () => { 9 | expect(slugifyLanguage('.Net')).toEqual('dotnet'); 10 | expect(slugifyLanguage('Cms-2')).toEqual('cms-2'); 11 | expect(slugifyLanguage('C#')).toEqual('csharp'); 12 | expect(slugifyLanguage('C++')).toEqual('cplusplus'); 13 | expect(slugifyLanguage('C--')).toEqual('cminusminus'); 14 | expect(slugifyLanguage('C*')).toEqual('cstar'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /apps/web/graphql/queries/get-repositories.graphql: -------------------------------------------------------------------------------- 1 | query GetRepositories( 2 | $first: Int = 12 3 | $after: String 4 | $searchTerm: String 5 | $languages: [String!] 6 | $order: RepoOrder 7 | $forkStatus: ForkStatusType 8 | $templateStatus: TemplateStatusType 9 | ) { 10 | repositories( 11 | first: $first 12 | after: $after 13 | searchTerm: $searchTerm 14 | languages: $languages 15 | order: $order 16 | forkStatus: $forkStatus 17 | templateStatus: $templateStatus 18 | ) { 19 | edges { 20 | node { 21 | ...repoPreview 22 | } 23 | } 24 | pageInfo { 25 | hasNextPage 26 | endCursor 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /apps/server/src/collection/collection.processor.ts: -------------------------------------------------------------------------------- 1 | import { Process, Processor } from '@nestjs/bull'; 2 | import { Logger } from '@nestjs/common'; 3 | import { MAIN_PROCESSES, MAIN_QUEUE } from '../queue'; 4 | import { CollectionService } from './collection.service'; 5 | 6 | @Processor(MAIN_QUEUE) 7 | export class CollectionProcessor { 8 | constructor(private readonly collector: CollectionService) {} 9 | private logger = new Logger(CollectionProcessor.name); 10 | 11 | @Process(MAIN_PROCESSES.COLLECT_COLLECTIONS) 12 | async collectProcess() { 13 | this.logger.log('Starting collecting the repositories...'); 14 | 15 | await this.collector.collectAllCollections(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /apps/web-e2e/src/support/index.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | -------------------------------------------------------------------------------- /apps/web/components/Feature/ClientOnlyPortal.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from 'react'; 2 | import { createPortal } from 'react-dom'; 3 | 4 | interface Props { 5 | children: React.ReactNode; 6 | selector: string; 7 | } 8 | 9 | // This is just an implementation of react portals but in the form of a component 10 | export function ClientOnlyPortal({ children, selector }: Props) { 11 | const ref = useRef(); 12 | const [mounted, setMounted] = useState(false); 13 | 14 | useEffect(() => { 15 | ref.current = document.querySelector(selector); 16 | setMounted(true); 17 | }, [selector]); 18 | 19 | return mounted ? createPortal(children, ref.current) : null; 20 | } 21 | -------------------------------------------------------------------------------- /apps/web/styles/global.css: -------------------------------------------------------------------------------- 1 | @tailwind components; 2 | @tailwind base; 3 | @tailwind utilities; 4 | 5 | ::-webkit-scrollbar { 6 | width: 10px; 7 | } 8 | 9 | /* Track */ 10 | ::-webkit-scrollbar-track { 11 | background-color: transparent; 12 | } 13 | 14 | /* Handle */ 15 | ::-webkit-scrollbar-thumb { 16 | @apply bg-transparent rounded bg-gray-400 dark:bg-gray-500; 17 | } 18 | 19 | /* Handle on hover */ 20 | ::-webkit-scrollbar-thumb:hover { 21 | @apply bg-gray-500 dark:bg-gray-400; 22 | } 23 | 24 | .text-secondary { 25 | @apply text-gray-700 dark:text-gray-300; 26 | } 27 | 28 | .prose img { 29 | display: inline; 30 | } 31 | 32 | * { 33 | @apply scroll-mt-24 scroll-smooth; 34 | } 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /apps/server/src/repository/enums/archive-status-type.enum.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from '@nestjs/graphql'; 2 | 3 | export enum ArchiveStatusType { 4 | ALL = 'ALL', 5 | ARCHIVED = 'ARCHIVED', 6 | NOT_ARCHIVED = 'NOT_ARCHIVED', 7 | } 8 | 9 | registerEnumType(ArchiveStatusType, { 10 | name: 'ArchiveStatusType', 11 | description: 'The repo type used in filters.', 12 | valuesMap: { 13 | ALL: { 14 | description: "Doesn't apply any filter to the query.", 15 | }, 16 | ARCHIVED: { 17 | description: 'Only returns the archived repositories.', 18 | }, 19 | NOT_ARCHIVED: { 20 | description: 'Only returns the non-archived repositories.', 21 | }, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /apps/server/src/repository/enums/template-status-type.enum.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from '@nestjs/graphql'; 2 | 3 | export enum TemplateStatusType { 4 | ALL = 'ALL', 5 | TEMPLATE = 'TEMPLATE', 6 | NOT_TEMPLATE = 'NOT_TEMPLATE', 7 | } 8 | 9 | registerEnumType(TemplateStatusType, { 10 | name: 'TemplateStatusType', 11 | description: 'The repo type used in filters.', 12 | valuesMap: { 13 | ALL: { 14 | description: "Doesn't apply any filter to the query.", 15 | }, 16 | TEMPLATE: { 17 | description: 'Only returns the template repositories.', 18 | }, 19 | NOT_TEMPLATE: { 20 | description: 'Only returns the non-template repositories.', 21 | }, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /apps/web/components/UI/Input/Input.tsx: -------------------------------------------------------------------------------- 1 | import { InputHTMLAttributes } from 'react'; 2 | import { CheckboxInput } from './CheckboxInput'; 3 | import { RadioInput } from './RadioInput'; 4 | import { TextareaInput } from './TextareaInput'; 5 | import { TextInputProps, TextInput } from './TextInput'; 6 | 7 | export interface InputProps extends InputHTMLAttributes { 8 | className?: string; 9 | } 10 | 11 | // Default input is set to text input 12 | export const Input = ({ ...props }: TextInputProps) => { 13 | return ; 14 | }; 15 | 16 | Input.Text = TextInput as any; 17 | Input.Checkbox = CheckboxInput; 18 | Input.Radio = RadioInput; 19 | Input.Textarea = TextareaInput; 20 | -------------------------------------------------------------------------------- /libs/common/src/lib/links.ts: -------------------------------------------------------------------------------- 1 | export const links = { 2 | instagram: 'https://instagram.com/matnbaz_net', 3 | discord: 'https://discord.link/matnbaz', 4 | telegram: 'https://t.me/matnbaz_net', 5 | github: 'https://github.com/matnbaz', 6 | githubRepo: 'https://github.com/matnbaz/matnbaz', 7 | githubRoadmap: 'https://github.com/orgs/matnbaz/projects/1/', 8 | visualIdentity: 'https://github.com/matnbaz/visual', 9 | twitter: 'https://twitter.com/matnbaz_net', 10 | twitterTweet: 11 | 'https://twitter.com/intent/tweet?text=%D8%A8%D9%87%20%D9%85%D8%AA%D9%86%E2%80%8C%D8%A8%D8%A7%D8%B2%20%DB%8C%DA%A9%20%D8%B3%D8%B1%DB%8C%20%D8%A8%D8%B2%D9%86%DB%8C%D8%AF.%20%0A&url=matnbaz.net&via=matnbaz_net', 12 | }; 13 | -------------------------------------------------------------------------------- /apps/web/components/SkeletonLoader/SkeletonLoaderShape.tsx: -------------------------------------------------------------------------------- 1 | import styles from './SkeletonLoaderShape.module.css'; 2 | enum ShapeClasses { 3 | 'circle' = 'rounded-full', 4 | 'rectangle' = '', 5 | } 6 | 7 | export interface SkeletonShapeProps { 8 | shape: keyof typeof ShapeClasses; 9 | width: string; 10 | height: string; 11 | className?: string; 12 | } 13 | 14 | export const SkeletonLoaderShape = ({ 15 | shape, 16 | width, 17 | height, 18 | className, 19 | }: SkeletonShapeProps) => { 20 | return ( 21 |
27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /apps/image-generator/src/post/post.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Query, Render, Res } from '@nestjs/common'; 2 | import { PostImageOptions, PostService } from './post.service'; 3 | 4 | @Controller() 5 | export class PostController { 6 | constructor(private readonly postService: PostService) {} 7 | 8 | @Get('/') 9 | @Render('index') 10 | generatorHtml() { 11 | return {}; 12 | } 13 | 14 | @Get('/generate') 15 | async postEndpoint(@Query() options: PostImageOptions, @Res() response) { 16 | const image = await this.postService.render(options); 17 | response.setHeader('Content-Type', 'image/jpeg'); 18 | const buffer = Buffer.from(image as string, 'base64'); 19 | response.send(buffer); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/server/src/app/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { AppController } from './app.controller'; 4 | import { AppService } from './app.service'; 5 | 6 | describe('AppController', () => { 7 | let app: TestingModule; 8 | 9 | beforeAll(async () => { 10 | app = await Test.createTestingModule({ 11 | controllers: [AppController], 12 | providers: [AppService], 13 | }).compile(); 14 | }); 15 | 16 | describe('getData', () => { 17 | it('should return "Welcome to api!"', () => { 18 | const appController = app.get(AppController); 19 | expect(appController.getData()).toEqual({ message: 'Welcome to api!' }); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /apps/web/components/Icons/MatnbazLogo.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | 3 | export const MatnbazLogo = ({ 4 | className, 5 | ...props 6 | }: React.SVGAttributes) => { 7 | return ( 8 | 14 | 15 | 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /apps/web/graphql/fragments/repo-full.fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment repoFull on Repository { 2 | id 3 | fullName 4 | name 5 | platformUrl 6 | platform 7 | descriptionLimited 8 | descriptionDirection 9 | archived 10 | isTemplate 11 | defaultBranch 12 | pushedAt { 13 | difference(persianNumbers: true) 14 | } 15 | createdAt { 16 | formatted(persianNumbers: true) 17 | } 18 | homePage 19 | stargazersCount 20 | forksCount 21 | openIssuesCount 22 | readmeHtml 23 | openGraphImageUrl 24 | language { 25 | name 26 | color { 27 | hexString 28 | } 29 | } 30 | license { 31 | name 32 | key 33 | spdxId 34 | } 35 | owner { 36 | type 37 | login 38 | platformId 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /prisma/migrations/20220111055624_add_node_id_to_repo_and_owner/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - A unique constraint covering the columns `[nodeId]` on the table `Owner` will be added. If there are existing duplicate values, this will fail. 5 | - A unique constraint covering the columns `[nodeId]` on the table `Repository` will be added. If there are existing duplicate values, this will fail. 6 | 7 | */ 8 | -- AlterTable 9 | ALTER TABLE "Owner" ADD COLUMN "nodeId" TEXT; 10 | 11 | -- AlterTable 12 | ALTER TABLE "Repository" ADD COLUMN "nodeId" TEXT; 13 | 14 | -- CreateIndex 15 | CREATE UNIQUE INDEX "Owner_nodeId_key" ON "Owner"("nodeId"); 16 | 17 | -- CreateIndex 18 | CREATE UNIQUE INDEX "Repository_nodeId_key" ON "Repository"("nodeId"); 19 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "importHelpers": true, 11 | "target": "es2015", 12 | "module": "esnext", 13 | "lib": ["es2017", "dom"], 14 | "skipLibCheck": true, 15 | "skipDefaultLibCheck": true, 16 | "baseUrl": ".", 17 | "paths": { 18 | "@matnbaz/common": ["libs/common/src/index.ts"], 19 | "@matnbaz/monomedia": ["libs/monomedia/src/index.ts"], 20 | "@matnbaz/telegraf": ["libs/telegraf/src/index.ts"] 21 | } 22 | }, 23 | "exclude": ["node_modules", "tmp"] 24 | } 25 | -------------------------------------------------------------------------------- /prisma/migrations/20220127155742_remove_selections/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `RepositorySelection` table. If the table is not empty, all the data it contains will be lost. 5 | - You are about to drop the `_RepositoryToRepositorySelection` table. If the table is not empty, all the data it contains will be lost. 6 | 7 | */ 8 | -- DropForeignKey 9 | ALTER TABLE "_RepositoryToRepositorySelection" DROP CONSTRAINT "_RepositoryToRepositorySelection_A_fkey"; 10 | 11 | -- DropForeignKey 12 | ALTER TABLE "_RepositoryToRepositorySelection" DROP CONSTRAINT "_RepositoryToRepositorySelection_B_fkey"; 13 | 14 | -- DropTable 15 | DROP TABLE "RepositorySelection"; 16 | 17 | -- DropTable 18 | DROP TABLE "_RepositoryToRepositorySelection"; 19 | -------------------------------------------------------------------------------- /apps/image-generator/src/main.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@nestjs/common'; 2 | import { NestFactory } from '@nestjs/core'; 3 | import { NestExpressApplication } from '@nestjs/platform-express'; 4 | import { join } from 'path'; 5 | import { AppModule } from './app/app.module'; 6 | 7 | async function bootstrap() { 8 | const app = await NestFactory.create(AppModule); 9 | const globalPrefix = ''; 10 | app.setGlobalPrefix(globalPrefix); 11 | app.setBaseViewsDir(join(__dirname, 'assets', 'views')); 12 | app.setViewEngine('hbs'); 13 | const port = process.env.IMAGE_GENERATOR_PORT || 4444; 14 | await app.listen(port, () => { 15 | Logger.log('Listening at http://localhost:' + port + '/' + globalPrefix); 16 | }); 17 | } 18 | 19 | bootstrap(); 20 | -------------------------------------------------------------------------------- /apps/server/src/collection/collection.scheduler.ts: -------------------------------------------------------------------------------- 1 | import { InjectQueue } from '@nestjs/bull'; 2 | import { Injectable, Logger } from '@nestjs/common'; 3 | import { Cron, CronExpression } from '@nestjs/schedule'; 4 | import { Queue } from 'bull'; 5 | import { MAIN_PROCESSES, MAIN_QUEUE } from '../queue'; 6 | 7 | @Injectable() 8 | export class CollectionScheduler { 9 | constructor(@InjectQueue(MAIN_QUEUE) private readonly queue: Queue) {} 10 | private logger = new Logger(CollectionScheduler.name); 11 | 12 | @Cron(CronExpression.EVERY_DAY_AT_6AM) 13 | async collect() { 14 | await this.queue.add(MAIN_PROCESSES.COLLECT_COLLECTIONS); 15 | this.logger.log( 16 | `The cronjob for collections got called, the job is now in the queue.` 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/server/src/repository/args/repo-filter.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field } from '@nestjs/graphql'; 2 | import { ArchiveStatusType } from '../enums/archive-status-type.enum'; 3 | import { ForkStatusType } from '../enums/fork-status-type.enum'; 4 | import { TemplateStatusType } from '../enums/template-status-type.enum'; 5 | 6 | @ArgsType() 7 | export class RepoFilterArgs { 8 | /** 9 | * Language slugs 10 | */ 11 | languages?: string[]; 12 | 13 | /** 14 | * License keys 15 | */ 16 | licenses?: string[]; 17 | 18 | @Field(() => TemplateStatusType) 19 | templateStatus?: TemplateStatusType; 20 | 21 | @Field(() => ForkStatusType) 22 | forkStatus?: ForkStatusType; 23 | 24 | @Field(() => ArchiveStatusType) 25 | archiveStatus?: ArchiveStatusType; 26 | } 27 | -------------------------------------------------------------------------------- /apps/github-bot/src/app/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { AppController } from './app.controller'; 4 | import { AppService } from './app.service'; 5 | 6 | describe('AppController', () => { 7 | let app: TestingModule; 8 | 9 | beforeAll(async () => { 10 | app = await Test.createTestingModule({ 11 | controllers: [AppController], 12 | providers: [AppService], 13 | }).compile(); 14 | }); 15 | 16 | describe('getData', () => { 17 | it('should return "Welcome to github-bot!"', () => { 18 | const appController = app.get(AppController); 19 | expect(appController.getData()).toEqual({ 20 | message: 'Welcome to github-bot!', 21 | }); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /apps/server/src/color/utils.ts: -------------------------------------------------------------------------------- 1 | import { Color } from '../models/color.model'; 2 | 3 | export const createColorObject = (color: string): Color => { 4 | return { 5 | hexString: color, 6 | }; 7 | }; 8 | 9 | export function colorHexToDecimal(hex: string) { 10 | const chunks = []; 11 | let tmp, i; 12 | hex = hex.substr(1); 13 | if (hex.length === 3) { 14 | tmp = hex.split(''); 15 | for (i = 0; i < 3; i++) { 16 | chunks.push(parseInt(tmp[i] + '' + tmp[i], 16)); 17 | } 18 | } else if (hex.length === 6) { 19 | tmp = hex.match(/.{2}/g); 20 | for (i = 0; i < 3; i++) { 21 | chunks.push(parseInt(tmp[i], 16)); 22 | } 23 | } else { 24 | throw new Error("'" + hex + "' is not a valid hex format"); 25 | } 26 | 27 | return chunks; 28 | } 29 | -------------------------------------------------------------------------------- /apps/image-generator/src/app/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { AppController } from './app.controller'; 4 | import { AppService } from './app.service'; 5 | 6 | describe('AppController', () => { 7 | let app: TestingModule; 8 | 9 | beforeAll(async () => { 10 | app = await Test.createTestingModule({ 11 | controllers: [AppController], 12 | providers: [AppService], 13 | }).compile(); 14 | }); 15 | 16 | describe('getData', () => { 17 | it('should return "Welcome to image-generator!"', () => { 18 | const appController = app.get(AppController); 19 | expect(appController.getData()).toEqual({ 20 | message: 'Welcome to image-generator!', 21 | }); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /apps/web-e2e/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "apps/web-e2e", 3 | "sourceRoot": "apps/web-e2e/src", 4 | "projectType": "application", 5 | "targets": { 6 | "e2e": { 7 | "executor": "@nrwl/cypress:cypress", 8 | "options": { 9 | "cypressConfig": "apps/web-e2e/cypress.json", 10 | "devServerTarget": "web:serve" 11 | }, 12 | "configurations": { 13 | "production": { 14 | "devServerTarget": "web:serve:production" 15 | } 16 | } 17 | }, 18 | "lint": { 19 | "executor": "@nrwl/linter:eslint", 20 | "outputs": ["{options.outputFile}"], 21 | "options": { 22 | "lintFilePatterns": ["apps/web-e2e/**/*.{js,ts}"] 23 | } 24 | } 25 | }, 26 | "tags": [], 27 | "implicitDependencies": ["web"] 28 | } 29 | -------------------------------------------------------------------------------- /apps/web/components/Layout/PageHeader.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | 3 | export interface PageHeaderProps { 4 | title: string; 5 | description?: string | JSX.Element; 6 | visuallyHidden?: boolean; 7 | } 8 | 9 | export const PageHeader = ({ 10 | title, 11 | visuallyHidden, 12 | description, 13 | }: PageHeaderProps) => { 14 | return ( 15 |
21 |

{title}

22 | {description && ( 23 |

24 | {description} 25 |

26 | )} 27 |
28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /apps/server/src/github-discoverer/github-discoverer.scheduler.ts: -------------------------------------------------------------------------------- 1 | import { InjectQueue } from '@nestjs/bull'; 2 | import { Injectable, Logger } from '@nestjs/common'; 3 | import { Cron, CronExpression } from '@nestjs/schedule'; 4 | import { Queue } from 'bull'; 5 | import { GITHUB_PROCESSES, GITHUB_QUEUE } from '../queue'; 6 | 7 | @Injectable() 8 | export class GithubDiscovererScheduler { 9 | constructor(@InjectQueue(GITHUB_QUEUE) private readonly queue: Queue) {} 10 | private logger = new Logger(GithubDiscovererScheduler.name); 11 | 12 | @Cron(CronExpression.EVERY_DAY_AT_1AM) 13 | async discover() { 14 | await this.queue.add(GITHUB_PROCESSES.DISCOVER); 15 | this.logger.log( 16 | `The cronjob for GitHub's discovery got called, the jobs are now in the queue.` 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/server/src/github-extractor/github-extractor.scheduler.ts: -------------------------------------------------------------------------------- 1 | import { InjectQueue } from '@nestjs/bull'; 2 | import { Injectable, Logger } from '@nestjs/common'; 3 | import { Cron, CronExpression } from '@nestjs/schedule'; 4 | import { Queue } from 'bull'; 5 | import { GITHUB_PROCESSES, GITHUB_QUEUE } from '../queue'; 6 | 7 | @Injectable() 8 | export class GithubExtractorScheduler { 9 | constructor(@InjectQueue(GITHUB_QUEUE) private readonly queue: Queue) {} 10 | private logger = new Logger(GithubExtractorScheduler.name); 11 | 12 | @Cron(CronExpression.EVERY_DAY_AT_4AM) 13 | async extract() { 14 | await this.queue.add(GITHUB_PROCESSES.EXTRACT); 15 | this.logger.log( 16 | `The cronjob for GitHub owner extraction got called, the job is now in the queue.` 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/server/src/github-extractor/github-extractor.processor.ts: -------------------------------------------------------------------------------- 1 | import { Process, Processor } from '@nestjs/bull'; 2 | import { Logger } from '@nestjs/common'; 3 | import { Job } from 'bull'; 4 | import { GITHUB_PROCESSES, GITHUB_QUEUE } from '../queue'; 5 | import { GithubExtractorService } from './github-extractor.service'; 6 | 7 | @Processor(GITHUB_QUEUE) 8 | export class GithubExtractorProcessor { 9 | constructor(private readonly extractorService: GithubExtractorService) {} 10 | private logger = new Logger(GithubExtractorProcessor.name); 11 | 12 | @Process(GITHUB_PROCESSES.EXTRACT) 13 | async extractProcess(job: Job<{ forceAll: boolean }>) { 14 | this.logger.log('Starting the extraction of repositories...'); 15 | 16 | await this.extractorService.fullExtract(job.data.forceAll || false); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /prisma/seed.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client'; 2 | const bcrypt = require('bcrypt'); 3 | 4 | const prisma = new PrismaClient(); 5 | 6 | const main = async () => { 7 | const admin1 = await prisma.user.create({ 8 | data: { 9 | email: 'johndoe@acme.test', 10 | username: 'johndoe', 11 | password: bcrypt.hashSync('password', 10), 12 | name: 'John Doe', 13 | type: 'Admin', 14 | }, 15 | }); 16 | console.log('Admin1 created.'); 17 | 18 | const mod1 = await prisma.user.create({ 19 | data: { 20 | email: 'janedoe@acme.test', 21 | username: 'janedoe', 22 | password: bcrypt.hashSync('password', 10), 23 | name: 'Jane Doe', 24 | type: 'Moderator', 25 | }, 26 | }); 27 | console.log('Moderator1 created.'); 28 | }; 29 | 30 | main(); 31 | -------------------------------------------------------------------------------- /apps/server/src/owner/enums/owner-order.enum.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from '@nestjs/graphql'; 2 | 3 | export enum OwnerOrder { 4 | CONTRIBUTIONS_DESC = 'CONTRIBUTIONS_DESC', 5 | PUBLIC_CONTRIBUTIONS_DESC = 'PUBLIC_CONTRIBUTIONS_DESC', 6 | FOLLOWERS_DESC = 'FOLLOWERS_DESC', 7 | } 8 | 9 | registerEnumType(OwnerOrder, { 10 | name: 'OwnerOrder', 11 | description: 'You can order repositories with one of these options.', 12 | valuesMap: { 13 | CONTRIBUTIONS_DESC: { 14 | description: 'Order by contributions count in descending order.', 15 | }, 16 | PUBLIC_CONTRIBUTIONS_DESC: { 17 | description: 'Order by public contributions count in descending order.', 18 | }, 19 | FOLLOWERS_DESC: { 20 | description: 'Order by followers count in descending order.', 21 | }, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /apps/server/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | }, 17 | /* 18 | This rule lets you do: 19 | 20 | requiresShipping: boolean = false 21 | 22 | so the NestJS Graphql CLI can parse it 23 | */ 24 | { 25 | "files": [ 26 | "**/*.model.ts", 27 | "**/*.input.ts", 28 | "**/*.payload.ts", 29 | "**/*.args.ts" 30 | ], 31 | "rules": { 32 | "@typescript-eslint/no-inferrable-types": 0 33 | } 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /apps/server/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (config, _context) => { 2 | const tsLoader = config.module.rules.find((r) => 3 | r.loader.includes('ts-loader') 4 | ); 5 | 6 | if (tsLoader) { 7 | tsLoader.options.transpileOnly = false; 8 | tsLoader.options.getCustomTransformers = (program) => { 9 | return { 10 | before: [ 11 | require('@nestjs/graphql/plugin').before( 12 | { 13 | introspectComments: true, 14 | typeFileNameSuffix: [ 15 | '.input.ts', 16 | '.args.ts', 17 | '.entity.ts', 18 | '.model.ts', 19 | '.payload.ts', 20 | ], 21 | }, 22 | program 23 | ), 24 | ], 25 | }; 26 | }; 27 | } 28 | 29 | return config; 30 | }; 31 | -------------------------------------------------------------------------------- /apps/web/pages/submit-user.tsx: -------------------------------------------------------------------------------- 1 | import { NextPage } from 'next'; 2 | import { NextSeo } from 'next-seo'; 3 | import { SubmitUserForm } from '../components/Form/SubmitUserForm'; 4 | import { MainLayout } from '../components/Layout/MainLayout'; 5 | import { PageHeader } from '../components/Layout/PageHeader'; 6 | import { Card } from '../components/UI/Card'; 7 | 8 | const SubmitUser: NextPage = () => { 9 | return ( 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | }; 22 | 23 | export default SubmitUser; 24 | -------------------------------------------------------------------------------- /apps/server/src/models/owner.model.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, Int, ObjectType } from '@nestjs/graphql'; 2 | import { OwnerType } from './enums/owner-type.enum'; 3 | import { PlatformType } from './enums/platform-type.enum'; 4 | import { Node } from './node.model'; 5 | 6 | @ObjectType({ implements: [Node] }) 7 | export class Owner { 8 | @Field(() => PlatformType) 9 | platform: PlatformType; 10 | 11 | @Field(() => ID) 12 | platformId: string; 13 | 14 | name?: string; 15 | login: string; 16 | 17 | @Field(() => OwnerType) 18 | type: OwnerType; 19 | 20 | @Field(() => Int) 21 | contributionsCount?: number; 22 | 23 | @Field(() => Int) 24 | publicContributionsCount?: number; 25 | 26 | @Field(() => Int) 27 | followersCount?: number; 28 | 29 | twitterUsername?: string; 30 | location?: string; 31 | company?: string; 32 | websiteUrl?: string; 33 | } 34 | -------------------------------------------------------------------------------- /apps/web/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import { NextPage } from 'next'; 2 | import { NextSeo } from 'next-seo'; 3 | import { MainLayout } from '../components/Layout/MainLayout'; 4 | import { Button } from '../components/UI/Button/Button'; 5 | import { Divider } from '../components/UI/Divider'; 6 | 7 | const Error404Page: NextPage = () => { 8 | return ( 9 | 10 | 11 | 12 |
13 |

یافت نشد

14 | 15 | صفحه‌ای که دنبال آن هستید وجود ندارد یا پاک شده است. 16 | 17 | 18 | برو به خانه 19 |
20 |
21 | ); 22 | }; 23 | 24 | export default Error404Page; 25 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": ["**/*"], 4 | "plugins": ["@nrwl/nx"], 5 | "overrides": [ 6 | { 7 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 8 | "rules": { 9 | "@nrwl/nx/enforce-module-boundaries": [ 10 | "error", 11 | { 12 | "enforceBuildableLibDependency": true, 13 | "allow": [], 14 | "depConstraints": [ 15 | { 16 | "sourceTag": "*", 17 | "onlyDependOnLibsWithTags": ["*"] 18 | } 19 | ] 20 | } 21 | ] 22 | } 23 | }, 24 | { 25 | "files": ["*.ts", "*.tsx"], 26 | "extends": ["plugin:@nrwl/nx/typescript"], 27 | "rules": {} 28 | }, 29 | { 30 | "files": ["*.js", "*.jsx"], 31 | "extends": ["plugin:@nrwl/nx/javascript"], 32 | "rules": {} 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /apps/server/src/queue.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * For general-purpose jobs 3 | */ 4 | export const MAIN_QUEUE = 'main-queue'; 5 | 6 | export enum MAIN_PROCESSES { 7 | COLLECT_COLLECTIONS = 'COLLECT_COLLECTIONS', 8 | ADD_SELECTION = 'ADD_SELECTION', 9 | FEATURE_SELECTION = 'FEATURE_SELECTION', 10 | PARSE_POST_README = 'PARSE_POST_README', 11 | PUBLISH_POST = 'PUBLISH_POST', 12 | } 13 | 14 | /** 15 | * Queue for GitHub-related jobs 16 | */ 17 | export const GITHUB_QUEUE = 'github-queue'; 18 | 19 | export enum GITHUB_PROCESSES { 20 | DISCOVER = 'DISCOVER', 21 | DISCOVER_BY_LOCATION = 'DISCOVER_BY_LOCATION', 22 | DISCOVER_BY_ORG_LOCATION = 'DISCOVER_BY_ORG_LOCATION', 23 | DISCOVER_BY_ORG_PRESENCE = 'DISCOVER_BY_ORG_PRESENCE', 24 | DISCOVER_BY_REPO_SEARCH = 'DISCOVER_BY_REPO_SEARCH', 25 | 26 | EXTRACT = 'EXTRACT', 27 | 28 | ADD_OWNER = 'ADD_OWNER', 29 | } 30 | // TODO: make these class based instead of enums? 31 | -------------------------------------------------------------------------------- /libs/telegraf/src/lib/interfaces/telegraf-module-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { ModuleMetadata, Type } from '@nestjs/common'; 2 | import { Telegraf } from 'telegraf'; 3 | 4 | export interface TelegrafModuleOptions { 5 | isGlobal?: boolean; 6 | botToken?: ConstructorParameters[0]; 7 | } 8 | 9 | export type AsyncTelegrafModuleOptions = Omit< 10 | TelegrafModuleOptions, 11 | 'isGlobal' 12 | >; 13 | 14 | export interface TelegrafOptionsFactory { 15 | createTelegrafOptions(): 16 | | Promise 17 | | AsyncTelegrafModuleOptions; 18 | } 19 | 20 | export interface TelegrafModuleAsyncOptions 21 | extends Pick { 22 | isGlobal?: boolean; 23 | useExisting?: Type; 24 | useClass?: Type; 25 | useFactory?: ( 26 | ...args: any[] 27 | ) => Promise | AsyncTelegrafModuleOptions; 28 | inject?: any[]; 29 | } 30 | -------------------------------------------------------------------------------- /apps/server/src/models/repository.model.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, Int, ObjectType } from '@nestjs/graphql'; 2 | import { PlatformType } from './enums/platform-type.enum'; 3 | import { Node } from './node.model'; 4 | 5 | @ObjectType({ implements: [Node] }) 6 | export class Repository { 7 | @Field(() => ID) 8 | platformId: string; 9 | 10 | homePage?: string; 11 | 12 | @Field(() => Int) 13 | size: number; 14 | 15 | @Field(() => Int) 16 | stargazersCount: number; 17 | 18 | @Field(() => Int) 19 | watchersCount: number; 20 | 21 | @Field(() => Int) 22 | forksCount: number; 23 | 24 | @Field(() => Int) 25 | openIssuesCount: number; 26 | 27 | archived: boolean; 28 | disabled: boolean; 29 | isTemplate: boolean; 30 | defaultBranch: string; 31 | name: string; 32 | description?: string; 33 | readme?: string; 34 | isFork: boolean; 35 | openGraphImageUrl?: string; 36 | 37 | @Field(() => PlatformType) 38 | platform: PlatformType; 39 | } 40 | -------------------------------------------------------------------------------- /apps/server/src/collection/collection.command.ts: -------------------------------------------------------------------------------- 1 | import { Command, CommandRunner, Option } from 'nest-commander'; 2 | import { CollectionProcessor } from './collection.processor'; 3 | import { CollectionScheduler } from './collection.scheduler'; 4 | 5 | @Command({ name: 'collect' }) 6 | export class CollectionCommand implements CommandRunner { 7 | constructor( 8 | private readonly collectionProcessor: CollectionProcessor, 9 | private readonly collectionScheduler: CollectionScheduler 10 | ) {} 11 | 12 | async run( 13 | passedParams: string[], 14 | options?: Record 15 | ): Promise { 16 | options.schedule 17 | ? await this.collectionScheduler.collect() 18 | : await this.collectionProcessor.collectProcess(); 19 | } 20 | 21 | @Option({ 22 | flags: '-s, --schedule', 23 | description: 'Schedules the job instead of running it immediately', 24 | }) 25 | parseSchedule(val: string) { 26 | return true; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /apps/web/components/Modal/SubmitUserModal.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import { useState } from 'react'; 3 | import { SubmitUserForm } from '../Form/SubmitUserForm'; 4 | import { Modal } from '../UI/Modal'; 5 | 6 | export interface SubmitUserModalProps { 7 | title?: string; 8 | className?: string; 9 | } 10 | 11 | export const SubmitUserModal = ({ 12 | title = 'ثبت کاربر', 13 | className, 14 | }: SubmitUserModalProps) => { 15 | const [show, setShow] = useState(false); 16 | return ( 17 | <> 18 | 26 | { 30 | setShow((previousState) => !previousState); 31 | }} 32 | > 33 | 34 | 35 | 36 | ); 37 | }; 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /apps/server/src/github-extractor/github-extractor.module.ts: -------------------------------------------------------------------------------- 1 | import { BullModule } from '@nestjs/bull'; 2 | import { Module } from '@nestjs/common'; 3 | import { MarkdownModule } from '../markdown/markdown.module'; 4 | import { GITHUB_QUEUE } from '../queue'; 5 | import { GithubExtractorCommand } from './github-extractor.command'; 6 | import { GithubExtractorProcessor } from './github-extractor.processor'; 7 | import { GithubExtractorScheduler } from './github-extractor.scheduler'; 8 | import { GithubExtractorService } from './github-extractor.service'; 9 | import { GithubReadmeExtractorService } from './github-readme-extractor.service'; 10 | 11 | @Module({ 12 | imports: [MarkdownModule, BullModule.registerQueue({ name: GITHUB_QUEUE })], 13 | providers: [ 14 | GithubExtractorProcessor, 15 | GithubExtractorScheduler, 16 | GithubExtractorService, 17 | GithubReadmeExtractorService, 18 | GithubExtractorCommand, 19 | ], 20 | exports: [GithubReadmeExtractorService], 21 | }) 22 | export class GithubExtractorModule {} 23 | -------------------------------------------------------------------------------- /apps/server/src/github-discoverer/github-discoverer.command.ts: -------------------------------------------------------------------------------- 1 | import { Command, CommandRunner, Option } from 'nest-commander'; 2 | import { GithubDiscovererScheduler } from './github-discoverer.scheduler'; 3 | import { GithubDiscoveryProcessor } from './github-discovery.processor'; 4 | 5 | @Command({ name: 'discover' }) 6 | export class GithubDiscoverCommand implements CommandRunner { 7 | constructor( 8 | private readonly githubOwnerProcessor: GithubDiscoveryProcessor, 9 | private readonly githubOwnerScheduler: GithubDiscovererScheduler 10 | ) {} 11 | 12 | async run( 13 | passedParams: string[], 14 | options?: Record 15 | ): Promise { 16 | options.schedule 17 | ? await this.githubOwnerScheduler.discover() 18 | : await this.githubOwnerProcessor.discoverProcess(); 19 | } 20 | 21 | @Option({ 22 | flags: '-s, --schedule', 23 | description: 'Schedules the job instead of running it immediately', 24 | }) 25 | parseSchedule(val: string) { 26 | return true; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "npmScope": "matnbaz", 3 | "affected": { 4 | "defaultBase": "main" 5 | }, 6 | "cli": { 7 | "defaultCollection": "@nrwl/nest" 8 | }, 9 | "implicitDependencies": { 10 | "package.json": { 11 | "dependencies": "*", 12 | "devDependencies": "*" 13 | }, 14 | ".eslintrc.json": "*" 15 | }, 16 | "tasksRunnerOptions": { 17 | "default": { 18 | "runner": "@nrwl/workspace/tasks-runners/default", 19 | "options": { 20 | "cacheableOperations": ["build", "lint", "test", "e2e"] 21 | } 22 | } 23 | }, 24 | "targetDependencies": { 25 | "build": [ 26 | { 27 | "target": "build", 28 | "projects": "dependencies" 29 | } 30 | ] 31 | }, 32 | "defaultProject": "server", 33 | "generators": { 34 | "@nrwl/react": { 35 | "application": { 36 | "babel": true 37 | } 38 | }, 39 | "@nrwl/next": { 40 | "application": { 41 | "style": "css", 42 | "linter": "eslint" 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /apps/web/components/UI/Input/TextareaInput.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import { TextareaHTMLAttributes } from 'react'; 3 | import { IconType } from 'react-icons/lib'; 4 | 5 | export interface TextInputProps 6 | extends TextareaHTMLAttributes { 7 | icon?: IconType; 8 | } 9 | 10 | export const TextareaInput = ({ 11 | className, 12 | icon, 13 | ...props 14 | }: TextInputProps) => { 15 | const IconComponent = icon; 16 | return ( 17 |
18 |