project: migrate to monorepo
This commit is contained in:
parent
9c8c418ae5
commit
7e1ed52725
3
.gitignore
vendored
3
.gitignore
vendored
@ -36,3 +36,6 @@ yarn-error.log*
|
||||
# Misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
|
||||
sync*.cmd
|
||||
1
.husky/pre-commit
Normal file
1
.husky/pre-commit
Normal file
@ -0,0 +1 @@
|
||||
pnpm lint-staged
|
||||
2
.npmrc
Normal file
2
.npmrc
Normal file
@ -0,0 +1,2 @@
|
||||
auto-install-peers = true
|
||||
enable-pre-post-scripts=true # Enable pre/post scripts (for postui:add)
|
||||
81
README.md
Normal file
81
README.md
Normal file
@ -0,0 +1,81 @@
|
||||
# Turborepo starter
|
||||
|
||||
This is an official starter Turborepo.
|
||||
|
||||
## Using this example
|
||||
|
||||
Run the following command:
|
||||
|
||||
```sh
|
||||
npx create-turbo@latest
|
||||
```
|
||||
|
||||
## What's inside?
|
||||
|
||||
This Turborepo includes the following packages/apps:
|
||||
|
||||
### Apps and Packages
|
||||
|
||||
- `docs`: a [Next.js](https://nextjs.org/) app
|
||||
- `web`: another [Next.js](https://nextjs.org/) app
|
||||
- `@repo/ui`: a stub React component library shared by both `web` and `docs` applications
|
||||
- `@repo/eslint-config`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`)
|
||||
- `@repo/typescript-config`: `tsconfig.json`s used throughout the monorepo
|
||||
|
||||
Each package/app is 100% [TypeScript](https://www.typescriptlang.org/).
|
||||
|
||||
### Utilities
|
||||
|
||||
This Turborepo has some additional tools already setup for you:
|
||||
|
||||
- [TypeScript](https://www.typescriptlang.org/) for static type checking
|
||||
- [ESLint](https://eslint.org/) for code linting
|
||||
- [Prettier](https://prettier.io) for code formatting
|
||||
|
||||
### Build
|
||||
|
||||
To build all apps and packages, run the following command:
|
||||
|
||||
```
|
||||
cd my-turborepo
|
||||
pnpm build
|
||||
```
|
||||
|
||||
### Develop
|
||||
|
||||
To develop all apps and packages, run the following command:
|
||||
|
||||
```
|
||||
cd my-turborepo
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### Remote Caching
|
||||
|
||||
Turborepo can use a technique known as [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching) to share cache artifacts across machines, enabling you to share build caches with your team and CI/CD pipelines.
|
||||
|
||||
By default, Turborepo will cache locally. To enable Remote Caching you will need an account with Vercel. If you don't have an account you can [create one](https://vercel.com/signup), then enter the following commands:
|
||||
|
||||
```
|
||||
cd my-turborepo
|
||||
npx turbo login
|
||||
```
|
||||
|
||||
This will authenticate the Turborepo CLI with your [Vercel account](https://vercel.com/docs/concepts/personal-accounts/overview).
|
||||
|
||||
Next, you can link your Turborepo to your Remote Cache by running the following command from the root of your Turborepo:
|
||||
|
||||
```
|
||||
npx turbo link
|
||||
```
|
||||
|
||||
## Useful Links
|
||||
|
||||
Learn more about the power of Turborepo:
|
||||
|
||||
- [Tasks](https://turbo.build/repo/docs/core-concepts/monorepos/running-tasks)
|
||||
- [Caching](https://turbo.build/repo/docs/core-concepts/caching)
|
||||
- [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching)
|
||||
- [Filtering](https://turbo.build/repo/docs/core-concepts/monorepos/filtering)
|
||||
- [Configuration Options](https://turbo.build/repo/docs/reference/configuration)
|
||||
- [CLI Usage](https://turbo.build/repo/docs/reference/command-line-reference)
|
||||
17
apps/bot/.prettierrc
Normal file
17
apps/bot/.prettierrc
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"arrowParens": "always",
|
||||
"bracketSameLine": false,
|
||||
"bracketSpacing": true,
|
||||
"endOfLine": "auto",
|
||||
"insertPragma": false,
|
||||
"jsxSingleQuote": false,
|
||||
"printWidth": 100,
|
||||
"proseWrap": "preserve",
|
||||
"quoteProps": "as-needed",
|
||||
"requirePragma": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "all",
|
||||
"useTabs": false
|
||||
}
|
||||
51
apps/bot/Dockerfile
Normal file
51
apps/bot/Dockerfile
Normal file
@ -0,0 +1,51 @@
|
||||
ARG NODE_VERSION=22
|
||||
ARG PROJECT=bot
|
||||
|
||||
# Alpine image
|
||||
FROM node:${NODE_VERSION}-alpine AS alpine
|
||||
RUN apk update
|
||||
RUN apk add --no-cache libc6-compat
|
||||
|
||||
FROM alpine as base
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN apk add --no-cache libc6-compat && \
|
||||
corepack enable && \
|
||||
pnpm install turbo dotenv-cli --global
|
||||
|
||||
FROM base AS pruner
|
||||
ARG PROJECT
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN turbo prune --scope=${PROJECT} --docker
|
||||
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=pruner /app/out/json/ .
|
||||
COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
|
||||
COPY --from=pruner /app/out/pnpm-workspace.yaml ./pnpm-workspace.yaml
|
||||
|
||||
RUN --mount=type=cache,id=pnpm,target=~/.pnpm-store pnpm install --prod --frozen-lockfile
|
||||
|
||||
COPY --from=pruner /app/out/full/ .
|
||||
|
||||
COPY turbo.json turbo.json
|
||||
COPY .env .env
|
||||
|
||||
RUN dotenv -e .env turbo run build --filter=${PROJECT}...
|
||||
RUN --mount=type=cache,id=pnpm,target=~/.pnpm-store pnpm prune --prod --no-optional
|
||||
RUN rm -rf ./**/*/src
|
||||
|
||||
FROM alpine AS runner
|
||||
ARG PROJECT
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 botuser
|
||||
USER botuser
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder --chown=nodejs:nodejs /app .
|
||||
WORKDIR /app/apps/${PROJECT}
|
||||
|
||||
CMD ["node", "dist/index.cjs"]
|
||||
16
apps/bot/eslint.config.js
Normal file
16
apps/bot/eslint.config.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { node } from '@repo/eslint-config/node';
|
||||
|
||||
/** @type {import("eslint").Linter.Config} */
|
||||
export default [
|
||||
...node,
|
||||
{
|
||||
ignores: ['**/types/**', '*.config.*'],
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
'unicorn/prevent-abbreviations': 'off',
|
||||
'canonical/id-match': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
1
apps/bot/lint-staged.config.mjs
Normal file
1
apps/bot/lint-staged.config.mjs
Normal file
@ -0,0 +1 @@
|
||||
export { default } from '@repo/lint-staged-config/config';
|
||||
43
apps/bot/package.json
Normal file
43
apps/bot/package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "bot",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "dist/index.cjs",
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"dev": "dotenv -e ../../.env.local tsx watch src/index.ts",
|
||||
"start": "node dist/index.cjs",
|
||||
"lint": "eslint",
|
||||
"lint-staged": "lint-staged"
|
||||
},
|
||||
"keywords": [
|
||||
"telegram",
|
||||
"bot",
|
||||
"tiktok",
|
||||
"video",
|
||||
"download"
|
||||
],
|
||||
"dependencies": {
|
||||
"@grammyjs/auto-chat-action": "^0.1.1",
|
||||
"@grammyjs/hydrate": "^1.4.1",
|
||||
"@grammyjs/i18n": "^1.1.2",
|
||||
"@grammyjs/parse-mode": "^2.2.0",
|
||||
"@grammyjs/types": "^3.21.0",
|
||||
"@tobyg74/tiktok-api-dl": "^1.3.4",
|
||||
"@types/node": "catalog:",
|
||||
"grammy": "^1.37.0",
|
||||
"pino": "^9.9.0",
|
||||
"pino-pretty": "^13.1.1",
|
||||
"tsup": "^8.5.0",
|
||||
"typescript": "catalog:",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@repo/eslint-config": "workspace:*",
|
||||
"@repo/lint-staged-config": "workspace:*",
|
||||
"@repo/typescript-config": "workspace:*",
|
||||
"dotenv-cli": "catalog:",
|
||||
"lint-staged": "catalog:",
|
||||
"tsx": "^4.20.4"
|
||||
}
|
||||
}
|
||||
7443
apps/bot/pnpm-lock.yaml
generated
Normal file
7443
apps/bot/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
13
apps/bot/src/bot/context.ts
Normal file
13
apps/bot/src/bot/context.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { type logger } from '@/utils/logger';
|
||||
import { type AutoChatActionFlavor } from '@grammyjs/auto-chat-action';
|
||||
import { type HydrateFlavor } from '@grammyjs/hydrate';
|
||||
import { type I18nFlavor } from '@grammyjs/i18n';
|
||||
import { type Context as DefaultContext } from 'grammy';
|
||||
|
||||
export type Context = HydrateFlavor<
|
||||
AutoChatActionFlavor &
|
||||
DefaultContext &
|
||||
I18nFlavor & {
|
||||
logger: typeof logger;
|
||||
}
|
||||
>;
|
||||
@ -1,20 +1,21 @@
|
||||
import { validateTikTokUrl } from '@/utils/urls';
|
||||
import type { Context } from '../context';
|
||||
/* eslint-disable consistent-return */
|
||||
import { type Context } from '../context';
|
||||
import { logHandle } from '../helpers/logging';
|
||||
import { Composer, InputFile } from 'grammy';
|
||||
import { validateTikTokUrl } from '@/utils/urls';
|
||||
import { Downloader } from '@tobyg74/tiktok-api-dl';
|
||||
import { Composer, InputFile } from 'grammy';
|
||||
|
||||
const composer = new Composer<Context>();
|
||||
|
||||
const feature = composer.chatType('private');
|
||||
|
||||
feature.on('message:text', logHandle('download-message'), async (ctx) => {
|
||||
feature.on('message:text', logHandle('download-message'), async (context) => {
|
||||
try {
|
||||
const url = ctx.message.text;
|
||||
const url = context.message.text;
|
||||
|
||||
if (!validateTikTokUrl(url)) return ctx.reply(ctx.t('invalid_url'));
|
||||
if (!validateTikTokUrl(url)) return context.reply(context.t('invalid_url'));
|
||||
|
||||
const { result, message } = await Downloader(url, { version: 'v3' });
|
||||
const { message, result } = await Downloader(url, { version: 'v3' });
|
||||
|
||||
if (message) throw new Error(message);
|
||||
|
||||
@ -22,20 +23,22 @@ feature.on('message:text', logHandle('download-message'), async (ctx) => {
|
||||
const imagesUrls = result?.images;
|
||||
|
||||
if (!videoUrl && !imagesUrls?.length) {
|
||||
return ctx.reply(ctx.t('invalid_download_urls'));
|
||||
return context.reply(context.t('invalid_download_urls'));
|
||||
}
|
||||
|
||||
if (result?.type === 'video' && videoUrl) {
|
||||
return ctx.replyWithVideo(new InputFile({ url: videoUrl }));
|
||||
return context.replyWithVideo(new InputFile({ url: videoUrl }));
|
||||
}
|
||||
|
||||
if (result?.type === 'image' && imagesUrls) {
|
||||
return ctx.replyWithMediaGroup(imagesUrls.map((image) => ({ media: image, type: 'photo' })));
|
||||
return context.replyWithMediaGroup(
|
||||
imagesUrls.map((image) => ({ media: image, type: 'photo' })),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
ctx.logger.error(error);
|
||||
context.logger.error(error);
|
||||
|
||||
return ctx.reply(ctx.t('generic'));
|
||||
return context.reply(context.t('generic'));
|
||||
}
|
||||
});
|
||||
|
||||
1
apps/bot/src/bot/features/index.ts
Normal file
1
apps/bot/src/bot/features/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './download';
|
||||
@ -1,6 +1,6 @@
|
||||
import type { Context } from '../context';
|
||||
import type { ErrorHandler } from 'grammy';
|
||||
import { type Context } from '../context';
|
||||
import { getUpdateInfo } from '../helpers/logging';
|
||||
import { type ErrorHandler } from 'grammy';
|
||||
|
||||
export const errorHandler: ErrorHandler<Context> = (error) => {
|
||||
const { ctx } = error;
|
||||
21
apps/bot/src/bot/helpers/logging.ts
Normal file
21
apps/bot/src/bot/helpers/logging.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { type Context } from '../context';
|
||||
import { type Update } from '@grammyjs/types';
|
||||
import { type Middleware } from 'grammy';
|
||||
|
||||
export function getUpdateInfo(context: Context): Omit<Update, 'update_id'> {
|
||||
const { update_id, ...update } = context.update;
|
||||
|
||||
return update;
|
||||
}
|
||||
|
||||
export function logHandle(id: string): Middleware<Context> {
|
||||
return (context, next) => {
|
||||
context.logger.info({
|
||||
msg: `Handle "${id}"`,
|
||||
...(id.startsWith('unhandled') ? { update: getUpdateInfo(context) } : {}),
|
||||
});
|
||||
|
||||
return next();
|
||||
};
|
||||
}
|
||||
@ -1,15 +1,14 @@
|
||||
import type { Context } from './context';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { type Context } from './context';
|
||||
import { I18n } from '@grammyjs/i18n';
|
||||
import path from 'node:path';
|
||||
|
||||
export const i18n = new I18n<Context>({
|
||||
defaultLocale: 'en',
|
||||
directory: path.resolve(process.cwd(), 'locales'),
|
||||
useSession: true,
|
||||
fluentBundleOptions: {
|
||||
useIsolating: false,
|
||||
},
|
||||
useSession: true,
|
||||
});
|
||||
|
||||
export const isMultipleLocales = i18n.locales.length > 1;
|
||||
@ -1,27 +1,28 @@
|
||||
import { Bot } from 'grammy';
|
||||
import { logger } from '@/utils/logger';
|
||||
import { Context } from './context';
|
||||
import { i18n } from './i18n';
|
||||
import { errorHandler } from './handlers/errors';
|
||||
/* eslint-disable n/callback-return */
|
||||
import { type Context } from './context';
|
||||
import * as features from './features';
|
||||
import { hydrate } from '@grammyjs/hydrate';
|
||||
import { errorHandler } from './handlers/errors';
|
||||
import { i18n } from './i18n';
|
||||
import { logger } from '@/utils/logger';
|
||||
import { autoChatAction } from '@grammyjs/auto-chat-action';
|
||||
import { hydrate } from '@grammyjs/hydrate';
|
||||
import { Bot } from 'grammy';
|
||||
|
||||
type Params = {
|
||||
token: string;
|
||||
type Parameters_ = {
|
||||
apiRoot: string;
|
||||
token: string;
|
||||
};
|
||||
|
||||
export function createBot({ token, apiRoot }: Params) {
|
||||
export function createBot({ apiRoot, token }: Parameters_) {
|
||||
const bot = new Bot<Context>(token, {
|
||||
client: {
|
||||
apiRoot,
|
||||
},
|
||||
});
|
||||
|
||||
bot.use(async (ctx, next) => {
|
||||
ctx.logger = logger.child({
|
||||
update_id: ctx.update.update_id,
|
||||
bot.use(async (context, next) => {
|
||||
context.logger = logger.child({
|
||||
update_id: context.update.update_id,
|
||||
});
|
||||
|
||||
await next();
|
||||
3
apps/bot/src/constants/regex.ts
Normal file
3
apps/bot/src/constants/regex.ts
Normal file
@ -0,0 +1,3 @@
|
||||
/* eslint-disable sonarjs/regex-complexity */
|
||||
export const TIKTOK_URL_REGEX =
|
||||
/https:\/\/(?:m|t|www|vm|vt|lite)?\.?tiktok\.com\/(.*\b(?:(?:usr|v|embed|user|video|photo)\/|\?shareId=|&item_id=)(\d+)|\w+)/u;
|
||||
@ -1,10 +1,10 @@
|
||||
import { createBot } from './bot';
|
||||
import { env } from './config/env';
|
||||
import { env as environment } from './config/env';
|
||||
import { logger } from './utils/logger';
|
||||
|
||||
const bot = createBot({
|
||||
token: env.BOT_TOKEN,
|
||||
apiRoot: env.TELEGRAM_API_ROOT,
|
||||
apiRoot: environment.TELEGRAM_API_ROOT,
|
||||
token: environment.BOT_TOKEN,
|
||||
});
|
||||
|
||||
// Stopping the bot when the Node.js process
|
||||
@ -12,6 +12,6 @@ const bot = createBot({
|
||||
process.once('SIGINT', () => bot.stop());
|
||||
process.once('SIGTERM', () => bot.stop());
|
||||
|
||||
bot.start({
|
||||
onStart: (bot) => logger.info(`Bot ${bot.username} started`),
|
||||
await bot.start({
|
||||
onStart: ({ username }) => logger.info(`Bot ${username} started`),
|
||||
});
|
||||
@ -1,12 +1,14 @@
|
||||
/* eslint-disable n/no-process-env */
|
||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||
import pino from 'pino';
|
||||
|
||||
export const logger = pino({
|
||||
transport: {
|
||||
target: 'pino-pretty',
|
||||
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
|
||||
options: {
|
||||
colorize: true,
|
||||
translateTime: true,
|
||||
},
|
||||
target: 'pino-pretty',
|
||||
},
|
||||
});
|
||||
@ -1,4 +1,4 @@
|
||||
import { TIKTOK_URL_REGEX } from "@/constants/regex";
|
||||
import { TIKTOK_URL_REGEX } from '@/constants/regex';
|
||||
|
||||
export function validateTikTokUrl(url: string) {
|
||||
return TIKTOK_URL_REGEX.test(url);
|
||||
16
apps/bot/tsconfig.json
Normal file
16
apps/bot/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "@repo/typescript-config/base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"outDir": "dist",
|
||||
"alwaysStrict": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "Node",
|
||||
"module": "esnext",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["."],
|
||||
"exclude": ["dist", "build", "node_modules"]
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import { node } from '@vchikalkin/eslint-config-awesome';
|
||||
|
||||
/** @type {import("eslint").Linter.Config} */
|
||||
export default [
|
||||
...node,
|
||||
{
|
||||
rules: {
|
||||
'unicorn/prevent-abbreviations': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
51
package.json
51
package.json
@ -1,42 +1,27 @@
|
||||
{
|
||||
"name": "next-downloader-bot",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "dist/index.cjs",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"dev": "dotenv -e .env.local tsx watch src/index.ts",
|
||||
"start": "node dist/index.cjs"
|
||||
"build": "turbo build",
|
||||
"dev": "turbo dev",
|
||||
"lint": "turbo lint -- --fix --max-warnings 0",
|
||||
"format": "prettier --end-of-line lf --write \"**/*.{ts,tsx,md,mjs}\"",
|
||||
"prepare": "husky",
|
||||
"lint-staged": "turbo lint-staged",
|
||||
"graphql:codegen": "turbo graphql:codegen",
|
||||
"test:unit": "turbo test:unit",
|
||||
"test:e2e": "turbo test:e2e"
|
||||
},
|
||||
"keywords": [
|
||||
"telegram",
|
||||
"bot",
|
||||
"tiktok",
|
||||
"video",
|
||||
"download"
|
||||
],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"packageManager": "pnpm@9.15.9+sha512.68046141893c66fad01c079231128e9afb89ef87e2691d69e4d40eee228988295fd4682181bae55b58418c3a253bde65a505ec7c5f9403ece5cc3cd37dcf2531",
|
||||
"dependencies": {
|
||||
"@grammyjs/auto-chat-action": "^0.1.1",
|
||||
"@grammyjs/hydrate": "^1.4.1",
|
||||
"@grammyjs/i18n": "^1.1.2",
|
||||
"@grammyjs/parse-mode": "^2.2.0",
|
||||
"@grammyjs/types": "^3.21.0",
|
||||
"@tobyg74/tiktok-api-dl": "^1.3.4",
|
||||
"@types/node": "^24.2.1",
|
||||
"grammy": "^1.37.0",
|
||||
"pino": "^9.9.0",
|
||||
"pino-pretty": "^13.1.1",
|
||||
"tsup": "^8.5.0",
|
||||
"zod": "^4.0.17"
|
||||
"husky": "catalog:",
|
||||
"turbo": "^2.3.2",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vchikalkin/eslint-config-awesome": "^2.2.2",
|
||||
"dotenv-cli": "^10.0.0",
|
||||
"eslint": "^9.17.0",
|
||||
"tsx": "^4.20.4",
|
||||
"typescript": "^5.9.2"
|
||||
"prettier": "catalog:"
|
||||
},
|
||||
"packageManager": "pnpm@9.15.9",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/eslint-config/README.md
Normal file
3
packages/eslint-config/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# `@turbo/eslint-config`
|
||||
|
||||
Collection of internal eslint configurations.
|
||||
20
packages/eslint-config/base.js
Normal file
20
packages/eslint-config/base.js
Normal file
@ -0,0 +1,20 @@
|
||||
import turboPlugin from 'eslint-plugin-turbo';
|
||||
|
||||
/**
|
||||
* A shared ESLint configuration for the repository.
|
||||
*
|
||||
* @type {import("eslint").Linter.Config}
|
||||
* */
|
||||
export const config = [
|
||||
{
|
||||
plugins: {
|
||||
turbo: turboPlugin,
|
||||
},
|
||||
rules: {
|
||||
'turbo/no-undeclared-env-vars': 'warn',
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ['**/dist/**', '**/turbo/**', '**/.turbo/**'],
|
||||
},
|
||||
];
|
||||
12
packages/eslint-config/node.js
Normal file
12
packages/eslint-config/node.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { config as baseConfig } from './base.js';
|
||||
import awesome from '@vchikalkin/eslint-config-awesome';
|
||||
|
||||
/**
|
||||
* A custom ESLint configuration for libraries that use TypeScript.
|
||||
*
|
||||
* @type {import("eslint").Linter.Config}
|
||||
* */
|
||||
export const node = [
|
||||
...baseConfig,
|
||||
...awesome['node'],
|
||||
];
|
||||
17
packages/eslint-config/package.json
Normal file
17
packages/eslint-config/package.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@repo/eslint-config",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"exports": {
|
||||
"./base": "./base.js",
|
||||
"./node": "./node.js",
|
||||
"./typescript": "./typescript.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vchikalkin/eslint-config-awesome": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"eslint-plugin-tailwindcss": "^3.17.5",
|
||||
"eslint-plugin-turbo": "^2.3.0"
|
||||
}
|
||||
}
|
||||
12
packages/eslint-config/typescript.js
Normal file
12
packages/eslint-config/typescript.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { config as baseConfig } from './base.js';
|
||||
import awesome from '@vchikalkin/eslint-config-awesome';
|
||||
|
||||
/**
|
||||
* A custom ESLint configuration for libraries that use TypeScript.
|
||||
*
|
||||
* @type {import("eslint").Linter.Config}
|
||||
* */
|
||||
export const typescript = [
|
||||
...baseConfig,
|
||||
...awesome['typescript'],
|
||||
];
|
||||
3
packages/lint-staged-config/config.mjs
Normal file
3
packages/lint-staged-config/config.mjs
Normal file
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
'*.{js,jsx,ts,tsx,md,json}': ['eslint --fix'],
|
||||
};
|
||||
13
packages/lint-staged-config/package.json
Normal file
13
packages/lint-staged-config/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@repo/lint-staged-config",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"exports": {
|
||||
"./config": "./config.mjs"
|
||||
}
|
||||
}
|
||||
@ -13,18 +13,7 @@
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"target": "ES2022",
|
||||
|
||||
"baseUrl": ".",
|
||||
"outDir": "dist",
|
||||
"alwaysStrict": true,
|
||||
"strict": true,
|
||||
// "moduleResolution": "Node",
|
||||
// "module": "CommonJS",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["."],
|
||||
"exclude": ["dist", "build", "node_modules"]
|
||||
"target": "ES2022"
|
||||
}
|
||||
}
|
||||
9
packages/typescript-config/package.json
Normal file
9
packages/typescript-config/package.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "@repo/typescript-config",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
4695
pnpm-lock.yaml
generated
4695
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
16
pnpm-workspace.yaml
Normal file
16
pnpm-workspace.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
packages:
|
||||
- apps/*
|
||||
- packages/*
|
||||
catalog:
|
||||
"@types/node": ^20
|
||||
"@vchikalkin/eslint-config-awesome": ^2.2.2
|
||||
dotenv-cli: ^7.4.4
|
||||
eslint: ^9.17.0
|
||||
husky: ^9.1.7
|
||||
lint-staged: ^15.2.10
|
||||
prettier: ^3.2.5
|
||||
rimraf: ^6.0.1
|
||||
typescript: ^5.7
|
||||
vite-tsconfig-paths: ^5.1.4
|
||||
vitest: ^2.1.8
|
||||
zod: ^3.24.1
|
||||
@ -1,13 +0,0 @@
|
||||
import { logger } from '@/utils/logger';
|
||||
import { HydrateFlavor } from '@grammyjs/hydrate';
|
||||
import { I18nFlavor } from '@grammyjs/i18n';
|
||||
import { Context as DefaultContext } from 'grammy';
|
||||
import type { AutoChatActionFlavor } from '@grammyjs/auto-chat-action';
|
||||
|
||||
export type Context = HydrateFlavor<
|
||||
DefaultContext &
|
||||
AutoChatActionFlavor &
|
||||
I18nFlavor & {
|
||||
logger: typeof logger;
|
||||
}
|
||||
>;
|
||||
@ -1 +0,0 @@
|
||||
export * from './download';
|
||||
@ -1,20 +0,0 @@
|
||||
import type { Context } from '../context';
|
||||
import type { Update } from '@grammyjs/types';
|
||||
import type { Middleware } from 'grammy';
|
||||
|
||||
export function getUpdateInfo(ctx: Context): Omit<Update, 'update_id'> {
|
||||
const { update_id, ...update } = ctx.update;
|
||||
|
||||
return update;
|
||||
}
|
||||
|
||||
export function logHandle(id: string): Middleware<Context> {
|
||||
return (ctx, next) => {
|
||||
ctx.logger.info({
|
||||
msg: `Handle "${id}"`,
|
||||
...(id.startsWith('unhandled') ? { update: getUpdateInfo(ctx) } : {}),
|
||||
});
|
||||
|
||||
return next();
|
||||
};
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
export const TIKTOK_URL_REGEX =
|
||||
/https:\/\/(?:m|t|www|vm|vt|lite)?\.?tiktok\.com\/((?:.*\b(?:(?:usr|v|embed|user|video|photo)\/|\?shareId=|\&item_id=)(\d+))|\w+)/;
|
||||
34
turbo.json
Normal file
34
turbo.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"$schema": "https://turbo.build/schema.json",
|
||||
"ui": "tui",
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": ["$TURBO_DEFAULT$", ".env*"],
|
||||
"outputs": [".next/**", "!.next/cache/**"],
|
||||
"env": ["BOT_TOKEN", "TELEGRAM_API_ROOT"]
|
||||
},
|
||||
"lint": {
|
||||
"dependsOn": ["^lint"]
|
||||
},
|
||||
"check-types": {
|
||||
"dependsOn": ["^check-types"]
|
||||
},
|
||||
"dev": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
},
|
||||
"lint-staged": {
|
||||
"cache": false
|
||||
},
|
||||
"graphql:codegen": {
|
||||
"cache": false
|
||||
},
|
||||
"test:unit": {
|
||||
"cache": false
|
||||
},
|
||||
"test:e2e": {
|
||||
"cache": false
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user