mirror of https://github.com/sylv/micro.git
build: smaller docker images
This commit is contained in:
parent
5b325e595c
commit
7e50fb043b
|
@ -1,24 +1,19 @@
|
||||||
*
|
.env
|
||||||
|
*.env
|
||||||
|
.microrc.yaml
|
||||||
|
.secrets
|
||||||
|
|
||||||
!example
|
Dockerfile
|
||||||
!packages
|
node_modules
|
||||||
|
example
|
||||||
|
data
|
||||||
|
.vscode
|
||||||
|
.github
|
||||||
|
*.md
|
||||||
|
|
||||||
!LICENSE
|
packages/*/.turbo
|
||||||
|
packages/*/data
|
||||||
!package.json
|
packages/*/node_modules
|
||||||
!tsconfig.json
|
packages/*/dist
|
||||||
!tsconfig.server.json
|
packages/*/.next
|
||||||
!tailwind.config.js
|
packages/*/.swc
|
||||||
!next-env.d.ts
|
|
||||||
!next.config.js
|
|
||||||
!postcss.config.js
|
|
||||||
!pnpm-lock.yaml
|
|
||||||
!pnpm-workspace.yaml
|
|
||||||
!tsup.config.ts
|
|
||||||
!wrapper.sh
|
|
||||||
!turbo.json
|
|
||||||
|
|
||||||
packages/api/data
|
|
||||||
packages/api/dist
|
|
||||||
packages/web/.next
|
|
||||||
packages/*/.turbo
|
|
|
@ -1,12 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
hooks: {
|
|
||||||
readPackage: (pkg, context) => {
|
|
||||||
if (pkg.name === "@mikro-orm/cli") {
|
|
||||||
delete pkg.dependencies["@mikro-orm/core"];
|
|
||||||
pkg.peerDependencies["@mikro-orm/core"] = pkg.peerDependencies["@mikro-orm/postgresql"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return pkg;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
66
Dockerfile
66
Dockerfile
|
@ -1,32 +1,56 @@
|
||||||
FROM node:16-slim AS builder
|
FROM node:16-alpine AS deps
|
||||||
RUN npm i -g pnpm@7 tsup
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
ENV NODE_ENV=development
|
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
|
RUN npm i -g pnpm
|
||||||
|
|
||||||
# install development dependencies
|
|
||||||
WORKDIR /usr/src/micro
|
WORKDIR /usr/src/micro
|
||||||
RUN apt update && apt install -y ffmpeg git
|
|
||||||
|
|
||||||
# copy package.jsons and install dependencies
|
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||||
# doing this before copying everything helps with caching
|
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
|
||||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json .
|
pnpm fetch
|
||||||
COPY packages/web/package.json packages/web/package.json
|
|
||||||
COPY packages/thumbnail-generator/package.json packages/thumbnail-generator/package.json
|
|
||||||
COPY packages/api/package.json packages/api/package.json
|
|
||||||
RUN pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
# copy sources and build app
|
|
||||||
|
|
||||||
|
|
||||||
|
FROM node:16-alpine AS builder
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
|
||||||
|
WORKDIR /usr/src/micro
|
||||||
|
|
||||||
|
RUN apk add --no-cache git
|
||||||
|
RUN npm i -g pnpm
|
||||||
|
|
||||||
|
COPY --from=deps /usr/src/micro .
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
RUN pnpm install --offline --frozen-lockfile
|
||||||
RUN pnpm build
|
RUN pnpm build
|
||||||
|
|
||||||
# prune unused packages
|
|
||||||
# RUN pnpm prune --prod
|
|
||||||
|
|
||||||
|
|
||||||
# run as the "node" user https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md#non-root-user
|
|
||||||
|
FROM node:16-alpine AS runner
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
ENV NODE_ENV production
|
||||||
|
|
||||||
|
WORKDIR /usr/src/micro
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
# copy file dependencies
|
||||||
|
COPY --from=builder /usr/src/micro/packages/web/public ./packages/web/public
|
||||||
|
COPY --from=builder /usr/src/micro/packages/web/next.config.js ./packages/web/next.config.js
|
||||||
|
|
||||||
|
# copy web server
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /usr/src/micro/packages/web/.next/standalone/ ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /usr/src/micro/packages/web/.next/static ./packages/web/.next/static/
|
||||||
|
|
||||||
|
# copy api
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /usr/src/micro/packages/api/dist ./packages/api/dist
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /usr/src/micro/packages/api/dist ./packages/api/dist
|
||||||
|
|
||||||
|
COPY wrapper.sh .
|
||||||
RUN chmod +x ./wrapper.sh
|
RUN chmod +x ./wrapper.sh
|
||||||
RUN chown node:node packages/api/src/schema.gql
|
|
||||||
USER node
|
|
||||||
ENV NODE_ENV=production
|
|
||||||
|
|
||||||
# define how we want to start the app
|
|
||||||
ENTRYPOINT ["./wrapper.sh"]
|
ENTRYPOINT ["./wrapper.sh"]
|
|
@ -17,6 +17,6 @@
|
||||||
"clean": "rm -rf ./packages/*/{tsconfig.tsbuildinfo,lib,dist,yarn-error.log,.next}"
|
"clean": "rm -rf ./packages/*/{tsconfig.tsbuildinfo,lib,dist,yarn-error.log,.next}"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"turbo": "^1.3.0"
|
"turbo": "1.3.2-canary.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,9 +10,8 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch": "tsup src/main.ts src/migrations/* --watch --onSuccess \"node dist/main.js\" --target node16",
|
"watch": "tsup src/main.ts src/migrations/* --watch --onSuccess \"node dist/main.js\" --target node16",
|
||||||
"build": "rm -rf ./dist && tsup src/main.ts src/migrations/* --dts --sourcemap --target node16",
|
"build": "rm -rf ./dist && ncc build src/main.ts -o dist --minify --transpile-only --v8-cache --no-source-map-register",
|
||||||
"lint": "eslint src --fix --cache",
|
"lint": "eslint src --fix --cache",
|
||||||
"start": "node ./dist/main.js",
|
|
||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -20,10 +19,10 @@
|
||||||
"@fastify/helmet": "^8.0.0",
|
"@fastify/helmet": "^8.0.0",
|
||||||
"@fastify/multipart": "^6.0.0",
|
"@fastify/multipart": "^6.0.0",
|
||||||
"@jenyus-org/nestjs-graphql-utils": "^1.6.4",
|
"@jenyus-org/nestjs-graphql-utils": "^1.6.4",
|
||||||
"@mikro-orm/core": "^5.2.0",
|
"@mikro-orm/core": "^5.2.2",
|
||||||
"@mikro-orm/migrations": "^5.2.0",
|
"@mikro-orm/migrations": "^5.2.2",
|
||||||
"@mikro-orm/nestjs": "^5.0.2",
|
"@mikro-orm/nestjs": "^5.0.2",
|
||||||
"@mikro-orm/postgresql": "^5.2.0",
|
"@mikro-orm/postgresql": "^5.2.2",
|
||||||
"@nestjs/common": "^8.4.4",
|
"@nestjs/common": "^8.4.4",
|
||||||
"@nestjs/core": "^8.4.4",
|
"@nestjs/core": "^8.4.4",
|
||||||
"@nestjs/graphql": "^10.0.16",
|
"@nestjs/graphql": "^10.0.16",
|
||||||
|
@ -52,9 +51,9 @@
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"ms": "^3.0.0-canary.1",
|
"ms": "^3.0.0-canary.1",
|
||||||
"nanoid": "^3.3.4",
|
"nanoid": "^3.3.4",
|
||||||
"nodemailer": "^6.7.5",
|
"nodemailer": "^6.7.6",
|
||||||
"normalize-url": "^6",
|
"normalize-url": "^6",
|
||||||
"passport": "^0.5.2",
|
"passport": "^0.6.0",
|
||||||
"passport-jwt": "^4.0.0",
|
"passport-jwt": "^4.0.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
|
@ -64,22 +63,23 @@
|
||||||
"xbytes": "^1.7.0"
|
"xbytes": "^1.7.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mikro-orm/cli": "^5.2.0",
|
"@mikro-orm/cli": "^5.2.2",
|
||||||
"@swc/core": "^1.2.206",
|
"@swc/core": "^1.2.208",
|
||||||
"@sylo-digital/scripts": "^1.0.2",
|
"@sylo-digital/scripts": "^1.0.2",
|
||||||
"@types/bcrypt": "^5.0.0",
|
"@types/bcrypt": "^5.0.0",
|
||||||
"@types/dedent": "^0.7.0",
|
"@types/dedent": "^0.7.0",
|
||||||
"@types/jest": "^28.1.3",
|
"@types/jest": "^28.1.4",
|
||||||
"@types/luxon": "^2.3.2",
|
"@types/luxon": "^2.3.2",
|
||||||
"@types/mime-types": "^2.1.1",
|
"@types/mime-types": "^2.1.1",
|
||||||
"@types/node": "16",
|
"@types/node": "16",
|
||||||
"@types/passport-jwt": "^3.0.6",
|
"@types/passport-jwt": "^3.0.6",
|
||||||
"@types/passport-local": "^1.0.34",
|
"@types/passport-local": "^1.0.34",
|
||||||
"@types/sharp": "^0.30.2",
|
"@types/sharp": "^0.30.2",
|
||||||
"jest": "^28.1.0",
|
"@vercel/ncc": "^0.34.0",
|
||||||
|
"jest": "^28.1.2",
|
||||||
"pretty-bytes": "^6.0.0",
|
"pretty-bytes": "^6.0.0",
|
||||||
"ts-node": "^10.7.0",
|
"ts-node": "^10.8.2",
|
||||||
"tsup": "^6.1.2",
|
"tsup": "^6.1.3",
|
||||||
"typescript": "^4.7.4"
|
"typescript": "^4.7.4"
|
||||||
},
|
},
|
||||||
"mikro-orm": {
|
"mikro-orm": {
|
||||||
|
|
|
@ -18,13 +18,14 @@ export const migrate = async (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const migrations = await migrator.getPendingMigrations();
|
const executedMigrations = await migrator.getExecutedMigrations();
|
||||||
if (!migrations[0]) {
|
const pendingMigrations = await migrator.getPendingMigrations();
|
||||||
ormLogger.debug(`No pending migrations`);
|
if (!pendingMigrations[0]) {
|
||||||
|
ormLogger.debug(`No pending migrations, ${executedMigrations.length} already executed`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ormLogger.log(`Migrating through ${migrations.length} migrations`);
|
ormLogger.log(`Migrating through ${pendingMigrations.length} migrations`);
|
||||||
await em.transactional(async (em) => {
|
await em.transactional(async (em) => {
|
||||||
if (!skipLock) await em.execute(`LOCK TABLE ${migrationsTableName} IN EXCLUSIVE MODE`);
|
if (!skipLock) await em.execute(`LOCK TABLE ${migrationsTableName} IN EXCLUSIVE MODE`);
|
||||||
await migrator.up({ transaction: em.getTransactionContext() });
|
await migrator.up({ transaction: em.getTransactionContext() });
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { checkThumbnailSupport } from '@ryanke/thumbnail-generator';
|
||||||
import { Exclude } from 'class-transformer';
|
import { Exclude } from 'class-transformer';
|
||||||
import mimeType from 'mime-types';
|
import mimeType from 'mime-types';
|
||||||
import { generateDeleteKey } from '../../helpers/generate-delete-key.helper';
|
import { generateDeleteKey } from '../../helpers/generate-delete-key.helper';
|
||||||
import { ResourceBase } from '../../types';
|
import { Resource } from '../../helpers/resource.entity-base';
|
||||||
import { Paginated } from '../../types/paginated.type';
|
import { Paginated } from '../../types/paginated.type';
|
||||||
import { Thumbnail } from '../thumbnail/thumbnail.entity';
|
import { Thumbnail } from '../thumbnail/thumbnail.entity';
|
||||||
import { User } from '../user/user.entity';
|
import { User } from '../user/user.entity';
|
||||||
|
@ -23,7 +23,7 @@ import { FileMetadata } from './file-metadata.embeddable';
|
||||||
|
|
||||||
@Entity({ tableName: 'files' })
|
@Entity({ tableName: 'files' })
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class File extends ResourceBase {
|
export class File extends Resource {
|
||||||
@PrimaryKey()
|
@PrimaryKey()
|
||||||
@Field(() => ID)
|
@Field(() => ID)
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
@ -2,12 +2,12 @@ import { Entity, IdentifiedReference, ManyToOne, OptionalProps, PrimaryKey, Prop
|
||||||
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||||
import { Exclude } from 'class-transformer';
|
import { Exclude } from 'class-transformer';
|
||||||
import { generateContentId } from '../../helpers/generate-content-id.helper';
|
import { generateContentId } from '../../helpers/generate-content-id.helper';
|
||||||
import { ResourceBase } from '../../types';
|
|
||||||
import { User } from '../user/user.entity';
|
import { User } from '../user/user.entity';
|
||||||
|
import { Resource } from '../../helpers/resource.entity-base';
|
||||||
|
|
||||||
@Entity({ tableName: 'links' })
|
@Entity({ tableName: 'links' })
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class Link extends ResourceBase {
|
export class Link extends Resource {
|
||||||
@PrimaryKey()
|
@PrimaryKey()
|
||||||
@Field(() => ID)
|
@Field(() => ID)
|
||||||
id: string = generateContentId();
|
id: string = generateContentId();
|
||||||
|
|
|
@ -5,13 +5,13 @@ import { IsBoolean, IsNumber, IsOptional, IsString, Length } from 'class-validat
|
||||||
import mime from 'mime-types';
|
import mime from 'mime-types';
|
||||||
import { config } from '../../config';
|
import { config } from '../../config';
|
||||||
import { generateContentId } from '../../helpers/generate-content-id.helper';
|
import { generateContentId } from '../../helpers/generate-content-id.helper';
|
||||||
import { ResourceBase } from '../../types';
|
import { Resource } from '../../helpers/resource.entity-base';
|
||||||
import { Paginated } from '../../types/paginated.type';
|
import { Paginated } from '../../types/paginated.type';
|
||||||
import { User } from '../user/user.entity';
|
import { User } from '../user/user.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'pastes' })
|
@Entity({ tableName: 'pastes' })
|
||||||
@ObjectType({ isAbstract: true })
|
@ObjectType({ isAbstract: true })
|
||||||
export class Paste extends ResourceBase {
|
export class Paste extends Resource {
|
||||||
@PrimaryKey()
|
@PrimaryKey()
|
||||||
@Field(() => ID)
|
@Field(() => ID)
|
||||||
id: string = generateContentId();
|
id: string = generateContentId();
|
||||||
|
|
|
@ -6,12 +6,11 @@ import { Args, Mutation, Parent, Query, ResolveField, Resolver } from '@nestjs/g
|
||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { paginate, parseCursor } from '../../helpers/pagination';
|
import { paginate, parseCursor } from '../../helpers/pagination';
|
||||||
import { File } from '../../types';
|
|
||||||
import { UserId } from '../auth/auth.decorators';
|
import { UserId } from '../auth/auth.decorators';
|
||||||
import { AuthService, TokenType } from '../auth/auth.service';
|
import { AuthService, TokenType } from '../auth/auth.service';
|
||||||
import { JWTAuthGuard } from '../auth/guards/jwt.guard';
|
import { JWTAuthGuard } from '../auth/guards/jwt.guard';
|
||||||
import type { JWTPayloadUser } from '../auth/strategies/jwt.strategy';
|
import type { JWTPayloadUser } from '../auth/strategies/jwt.strategy';
|
||||||
import { FilePage } from '../file/file.entity';
|
import { File, FilePage } from '../file/file.entity';
|
||||||
import { InviteService } from '../invite/invite.service';
|
import { InviteService } from '../invite/invite.service';
|
||||||
import { Paste, PastePage } from '../paste/paste.entity';
|
import { Paste, PastePage } from '../paste/paste.entity';
|
||||||
import { CreateUserDto } from './dto/create-user.dto';
|
import { CreateUserDto } from './dto/create-user.dto';
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
import { EntityRepository, QueryOrder, UniqueConstraintViolationException } from '@mikro-orm/core';
|
import { EntityRepository, QueryOrder, UniqueConstraintViolationException } from '@mikro-orm/core';
|
||||||
import { InjectRepository } from '@mikro-orm/nestjs';
|
import { InjectRepository } from '@mikro-orm/nestjs';
|
||||||
import {
|
import { BadRequestException, ConflictException, ForbiddenException, Injectable } from '@nestjs/common';
|
||||||
BadRequestException,
|
|
||||||
ConflictException,
|
|
||||||
ForbiddenException,
|
|
||||||
Injectable
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { Cron, CronExpression } from '@nestjs/schedule';
|
import { Cron, CronExpression } from '@nestjs/schedule';
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from 'bcrypt';
|
||||||
import { readFileSync } from 'fs';
|
import dedent from 'dedent';
|
||||||
import { compile } from 'handlebars';
|
import { compile } from 'handlebars';
|
||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
|
@ -25,12 +20,18 @@ import type { Pagination } from './dto/pagination.dto';
|
||||||
import { UserVerification } from './user-verification.entity';
|
import { UserVerification } from './user-verification.entity';
|
||||||
import { User } from './user.entity';
|
import { User } from './user.entity';
|
||||||
|
|
||||||
|
const EMAIL_TEMPLATE_SOURCE = dedent`
|
||||||
|
<body>
|
||||||
|
<h1>Verify Your Email</h1>
|
||||||
|
<p>Thanks for signing up to micro. Click the link below to verify your email and activate your account.</p>
|
||||||
|
<a href="{{verifyUrl}}">{{verifyUrl}}</a>
|
||||||
|
<p><i>If you did not sign up for micro, ignore this email.</i></p>
|
||||||
|
`;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
private static readonly VERIFICATION_EXPIRY = ms('6 hours');
|
private static readonly VERIFICATION_EXPIRY = ms('6 hours');
|
||||||
private static readonly EMAIL_TEMPLATE = compile<{ verifyUrl: string }>(
|
private static readonly EMAIL_TEMPLATE = compile<{ verifyUrl: string }>(EMAIL_TEMPLATE_SOURCE);
|
||||||
readFileSync('templates/verify.handlebars', 'utf8')
|
|
||||||
);
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(User) private readonly userRepo: EntityRepository<User>,
|
@InjectRepository(User) private readonly userRepo: EntityRepository<User>,
|
||||||
|
@ -42,7 +43,7 @@ export class UserService {
|
||||||
|
|
||||||
async getUser(id: string, verified: boolean) {
|
async getUser(id: string, verified: boolean) {
|
||||||
const user = await this.userRepo.findOneOrFail(id);
|
const user = await this.userRepo.findOneOrFail(id);
|
||||||
if ( verified && config.email && !user.verifiedEmail) {
|
if (verified && config.email && !user.verifiedEmail) {
|
||||||
throw new ForbiddenException('You must verify your email first.');
|
throw new ForbiddenException('You must verify your email first.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
import type { AppController } from './modules/app.controller';
|
|
||||||
import type { File } from './modules/file/file.entity';
|
|
||||||
import type { InviteController } from './modules/invite/invite.controller';
|
|
||||||
import type { CreatePasteDto, Paste } from './modules/paste/paste.entity';
|
|
||||||
import type { UserController } from './modules/user/user.controller';
|
|
||||||
|
|
||||||
export type Await<T> = T extends {
|
|
||||||
then: (onfulfilled?: (value: infer U) => unknown) => unknown;
|
|
||||||
}
|
|
||||||
? U
|
|
||||||
: T;
|
|
||||||
|
|
||||||
// invite
|
|
||||||
export type GetInviteData = Await<ReturnType<InviteController['getInvite']>>;
|
|
||||||
|
|
||||||
// user
|
|
||||||
export type GetUserData = Await<ReturnType<UserController['getUser']>>;
|
|
||||||
export type GetUserFilesData = Await<ReturnType<UserController['getUserFiles']>>;
|
|
||||||
export type GetUserPastesData = Await<ReturnType<UserController['getUserPastes']>>;
|
|
||||||
export type GetUploadTokenData = Await<ReturnType<UserController['getUserToken']>>;
|
|
||||||
export type PutUploadTokenData = Await<ReturnType<UserController['resetUserToken']>>;
|
|
||||||
|
|
||||||
// file
|
|
||||||
export type GetFileData = File;
|
|
||||||
|
|
||||||
// app
|
|
||||||
export type GetServerConfigData = Await<ReturnType<AppController['getConfig']>>;
|
|
||||||
|
|
||||||
export type GetPasteData = Paste;
|
|
||||||
export type CreatePasteBody = CreatePasteDto;
|
|
||||||
|
|
||||||
export { Resource as ResourceBase } from './helpers/resource.entity-base';
|
|
||||||
export type { ResourcePaths } from './helpers/resource.entity-base';
|
|
||||||
export { File } from './modules/file/file.entity';
|
|
||||||
export { User } from './modules/user/user.entity';
|
|
|
@ -1,5 +0,0 @@
|
||||||
<body>
|
|
||||||
<h1>Verify Your Email</h1>
|
|
||||||
<p>Thanks for signing up to micro. Click the link below to verify your email and activate your account.</p>
|
|
||||||
<a href="{{verifyUrl}}">{{verifyUrl}}</a>
|
|
||||||
<p><i>If you did not sign up for micro, ignore this email.</i></p>
|
|
|
@ -21,13 +21,13 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/fluent-ffmpeg": "^2.1.20",
|
"@types/fluent-ffmpeg": "^2.1.20",
|
||||||
"@types/jest": "^28.1.3",
|
"@types/jest": "^28.1.4",
|
||||||
"@types/mime-types": "^2.1.1",
|
"@types/mime-types": "^2.1.1",
|
||||||
"@types/node": "^16.11.21",
|
"@types/node": "^16.11.21",
|
||||||
"@types/sharp": "^0.30.2",
|
"@types/sharp": "^0.30.2",
|
||||||
"eslint": "^8.16.0",
|
"eslint": "^8.19.0",
|
||||||
"jest": "^28.1.0",
|
"jest": "^28.1.2",
|
||||||
"tsup": "^6.1.2",
|
"tsup": "^6.1.3",
|
||||||
"typescript": "^4.7.4"
|
"typescript": "^4.7.4"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
output: 'standalone',
|
||||||
|
experimental: {
|
||||||
|
outputFileTracingRoot: path.join(__dirname, '../../'),
|
||||||
|
},
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch": "NODE_ENV=development concurrently \"next dev\" \"pnpm generate --watch\"",
|
"watch": "NODE_ENV=development concurrently \"next dev\" \"pnpm generate --watch\"",
|
||||||
"start": "NODE_ENV=production next start",
|
|
||||||
"build": "NODE_ENV=production next build",
|
"build": "NODE_ENV=production next build",
|
||||||
"lint": "NODE_ENV=production next lint",
|
"lint": "NODE_ENV=production next lint",
|
||||||
"generate": "graphql-codegen --config codegen.yml"
|
"generate": "graphql-codegen --config codegen.yml"
|
||||||
|
@ -28,9 +27,9 @@
|
||||||
"graphql": "^16.5.0",
|
"graphql": "^16.5.0",
|
||||||
"http-status-codes": "^2.2.0",
|
"http-status-codes": "^2.2.0",
|
||||||
"nanoid": "^3.3.4",
|
"nanoid": "^3.3.4",
|
||||||
"next": "12.1.6",
|
"next": "12.2.0",
|
||||||
"postcss": "^8.4.13",
|
"postcss": "^8.4.13",
|
||||||
"prism-react-renderer": "^1.3.1",
|
"prism-react-renderer": "^1.3.5",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "^18.1.0",
|
"react-dom": "^18.1.0",
|
||||||
"react-feather": "^2.0.9",
|
"react-feather": "^2.0.9",
|
||||||
|
@ -39,20 +38,20 @@
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
"swr": "^1.3.0",
|
"swr": "^1.3.0",
|
||||||
"tailwindcss": "^3.0.24",
|
"tailwindcss": "^3.0.24",
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
|
"concurrently": "^7.2.2",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"yup": "^0.32.11"
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@graphql-codegen/cli": "^2.6.2",
|
"@graphql-codegen/cli": "^2.7.0",
|
||||||
"@graphql-codegen/typescript": "2.5.1",
|
"@graphql-codegen/typescript": "2.6.0",
|
||||||
"@graphql-codegen/typescript-operations": "2.4.2",
|
"@graphql-codegen/typescript-operations": "2.4.3",
|
||||||
"@graphql-codegen/typescript-react-apollo": "3.2.16",
|
"@graphql-codegen/typescript-react-apollo": "3.2.17",
|
||||||
"@sylo-digital/scripts": "^1.0.2",
|
"@sylo-digital/scripts": "^1.0.2",
|
||||||
"@types/lodash": "^4.14.182",
|
"@types/lodash": "^4.14.182",
|
||||||
"@types/node": "16",
|
"@types/node": "16",
|
||||||
"@types/react": "^18.0.8",
|
"@types/react": "^18.0.8",
|
||||||
"concurrently": "^7.2.2",
|
|
||||||
"deepmerge": "^4.2.2",
|
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"typescript": "^4.7.4"
|
"typescript": "^4.7.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
2349
pnpm-lock.yaml
2349
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
||||||
],
|
],
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"dist/**",
|
"dist/**",
|
||||||
".next/**"
|
".next/{server,cache,src,static}"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
cd packages/api && npm run start &
|
cd packages/api && node ./dist/index.js &
|
||||||
cd packages/web && npm run start &
|
cd packages/web && node ./server.js &
|
||||||
|
|
||||||
wait -n
|
wait -n
|
||||||
exit $?
|
exit $?
|
Loading…
Reference in New Issue