Compare commits
6 Commits
main
...
feat/pw-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10f033fe78 | ||
| 869b2e696f | |||
| 050c970e7e | |||
| 5c83235cba | |||
| 3b2ca9963b | |||
| 0a08193418 |
@@ -10,3 +10,5 @@ SCORO_URL=ws://localhost:6543
|
|||||||
GOOGLE_CLIENT_ID=toto
|
GOOGLE_CLIENT_ID=toto
|
||||||
GOOGLE_SECRET=tata
|
GOOGLE_SECRET=tata
|
||||||
GOOGLE_CALLBACK_URL=http://localhost:19006/logged/google
|
GOOGLE_CALLBACK_URL=http://localhost:19006/logged/google
|
||||||
|
SMTP_TRANSPORT=
|
||||||
|
MAIL_AUTHOR='"Chromacase" <chromacase@octohub.app>'
|
||||||
|
|||||||
5
.envrc
5
.envrc
@@ -1,4 +1 @@
|
|||||||
if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then
|
use nix
|
||||||
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs="
|
|
||||||
fi
|
|
||||||
use flake
|
|
||||||
|
|||||||
4994
back/package-lock.json
generated
4994
back/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@
|
|||||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@nestjs-modules/mailer": "^1.9.1",
|
||||||
"@nestjs/common": "^10.1.0",
|
"@nestjs/common": "^10.1.0",
|
||||||
"@nestjs/config": "^3.0.0",
|
"@nestjs/config": "^3.0.0",
|
||||||
"@nestjs/core": "^10.1.0",
|
"@nestjs/core": "^10.1.0",
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
"node-fetch": "^2.6.12",
|
"node-fetch": "^2.6.12",
|
||||||
|
"nodemailer": "^6.9.5",
|
||||||
"passport-google-oauth20": "^2.0.0",
|
"passport-google-oauth20": "^2.0.0",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
@@ -53,6 +55,7 @@
|
|||||||
"@types/jest": "29.5.3",
|
"@types/jest": "29.5.3",
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
"@types/node": "^20.4.4",
|
"@types/node": "^20.4.4",
|
||||||
|
"@types/nodemailer": "^6.4.9",
|
||||||
"@types/passport-google-oauth20": "^2.0.11",
|
"@types/passport-google-oauth20": "^2.0.11",
|
||||||
"@types/supertest": "^2.0.12",
|
"@types/supertest": "^2.0.12",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
||||||
|
|||||||
2
back/prisma/migrations/20230907141258_/migration.sql
Normal file
2
back/prisma/migrations/20230907141258_/migration.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "User" ADD COLUMN "emailVerified" BOOLEAN NOT NULL DEFAULT false;
|
||||||
@@ -14,6 +14,7 @@ model User {
|
|||||||
username String @unique
|
username String @unique
|
||||||
password String?
|
password String?
|
||||||
email String
|
email String
|
||||||
|
emailVerified Boolean @default(false)
|
||||||
googleID String? @unique
|
googleID String? @unique
|
||||||
isGuest Boolean @default(false)
|
isGuest Boolean @default(false)
|
||||||
partyPlayed Int @default(0)
|
partyPlayed Int @default(0)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { ArtistModule } from './artist/artist.module';
|
|||||||
import { AlbumModule } from './album/album.module';
|
import { AlbumModule } from './album/album.module';
|
||||||
import { SearchModule } from './search/search.module';
|
import { SearchModule } from './search/search.module';
|
||||||
import { HistoryModule } from './history/history.module';
|
import { HistoryModule } from './history/history.module';
|
||||||
|
import { MailerModule } from '@nestjs-modules/mailer';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -28,6 +29,12 @@ import { HistoryModule } from './history/history.module';
|
|||||||
SearchModule,
|
SearchModule,
|
||||||
SettingsModule,
|
SettingsModule,
|
||||||
HistoryModule,
|
HistoryModule,
|
||||||
|
MailerModule.forRoot({
|
||||||
|
transport: process.env.SMTP_TRANSPORT,
|
||||||
|
defaults: {
|
||||||
|
from: process.env.MAIL_AUTHOR,
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService, PrismaService, ArtistService],
|
providers: [AppService, PrismaService, ArtistService],
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
HttpStatus,
|
HttpStatus,
|
||||||
ParseFilePipeBuilder,
|
ParseFilePipeBuilder,
|
||||||
Response,
|
Response,
|
||||||
|
Query,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { JwtAuthGuard } from './jwt-auth.guard';
|
import { JwtAuthGuard } from './jwt-auth.guard';
|
||||||
@@ -71,12 +72,45 @@ export class AuthController {
|
|||||||
try {
|
try {
|
||||||
const user = await this.usersService.createUser(registerDto);
|
const user = await this.usersService.createUser(registerDto);
|
||||||
await this.settingsService.createUserSetting(user.id);
|
await this.settingsService.createUserSetting(user.id);
|
||||||
|
await this.authService.sendVerifyMail(user);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HttpCode(200)
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Put('reset')
|
||||||
|
async password_reset(@Request() req: any, @Query('token') token: string): Promise<void> {
|
||||||
|
if (await this.authService.resetPassword(req.user.id, token))
|
||||||
|
return;
|
||||||
|
throw new BadRequestException("Invalid token. Expired or invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(200)
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Put('send-reset')
|
||||||
|
async send_reset(@Request() req: any): Promise<void> {
|
||||||
|
await this.authService.sendResetMail(req.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(200)
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Put('verify')
|
||||||
|
async verify(@Request() req: any, @Query('token') token: string): Promise<void> {
|
||||||
|
if (await this.authService.verifyMail(req.user.id, token))
|
||||||
|
return;
|
||||||
|
throw new BadRequestException("Invalid token. Expired or invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(200)
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Put('reverify')
|
||||||
|
async reverify(@Request() req: any): Promise<void> {
|
||||||
|
await this.authService.sendVerifyMail(req.user);
|
||||||
|
}
|
||||||
|
|
||||||
@ApiBody({ type: LoginDto })
|
@ApiBody({ type: LoginDto })
|
||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
@UseGuards(LocalAuthGuard)
|
@UseGuards(LocalAuthGuard)
|
||||||
@@ -121,7 +155,7 @@ export class AuthController {
|
|||||||
)
|
)
|
||||||
file: Express.Multer.File,
|
file: Express.Multer.File,
|
||||||
) {
|
) {
|
||||||
const path = `/data/${req.user.id}.jpg`
|
const path = `/data/${req.user.id}.jpg`;
|
||||||
writeFile(path, file.buffer, (err) => {
|
writeFile(path, file.buffer, (err) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { UsersService } from '../users/users.service';
|
import { UsersService } from '../users/users.service';
|
||||||
import { JwtService } from '@nestjs/jwt';
|
import { JwtService } from '@nestjs/jwt';
|
||||||
import * as bcrypt from 'bcryptjs';
|
import * as bcrypt from 'bcryptjs';
|
||||||
import PayloadInterface from './interface/payload.interface';
|
import PayloadInterface from './interface/payload.interface';
|
||||||
|
import { User } from 'src/models/user';
|
||||||
|
import { MailerService } from '@nestjs-modules/mailer';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
constructor(
|
constructor(
|
||||||
private userService: UsersService,
|
private userService: UsersService,
|
||||||
private jwtService: JwtService,
|
private jwtService: JwtService,
|
||||||
|
private emailService: MailerService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async validateUser(
|
async validateUser(
|
||||||
@@ -31,4 +34,33 @@ export class AuthService {
|
|||||||
access_token,
|
access_token,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendVerifyMail(user: User) {
|
||||||
|
const token = await this.jwtService.signAsync(
|
||||||
|
{
|
||||||
|
userId: user.id,
|
||||||
|
},
|
||||||
|
{ expiresIn: '10h' },
|
||||||
|
);
|
||||||
|
await this.emailService.sendMail({
|
||||||
|
to: user.email,
|
||||||
|
from: 'chromacase@octohub.app',
|
||||||
|
subject: 'Mail verification for Chromacase',
|
||||||
|
html: `To verify your mail, please click on this <a href="{${process.env.PUBLIC_URL}/verify?token=${token}">link</a>.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyMail(userId: number, token: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await this.jwtService.verifyAsync(token);
|
||||||
|
} catch(e) {
|
||||||
|
console.log("Verify mail token failure", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
await this.userService.updateUser({
|
||||||
|
where: { id: userId },
|
||||||
|
data: { emailVerified: true },
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
Injectable,
|
Injectable,
|
||||||
InternalServerErrorException,
|
InternalServerErrorException,
|
||||||
NotFoundException,
|
|
||||||
StreamableFile,
|
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { User, Prisma } from '@prisma/client';
|
import { User, Prisma } from '@prisma/client';
|
||||||
import { PrismaService } from 'src/prisma/prisma.service';
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
@@ -13,7 +11,9 @@ import fetch from 'node-fetch';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UsersService {
|
export class UsersService {
|
||||||
constructor(private prisma: PrismaService) {}
|
constructor(
|
||||||
|
private prisma: PrismaService,
|
||||||
|
) {}
|
||||||
|
|
||||||
async user(
|
async user(
|
||||||
userWhereUniqueInput: Prisma.UserWhereUniqueInput,
|
userWhereUniqueInput: Prisma.UserWhereUniqueInput,
|
||||||
@@ -95,8 +95,7 @@ export class UsersService {
|
|||||||
const resp = await fetch(
|
const resp = await fetch(
|
||||||
`https://www.gravatar.com/avatar/${hash}.jpg?d=404&s=200`,
|
`https://www.gravatar.com/avatar/${hash}.jpg?d=404&s=200`,
|
||||||
);
|
);
|
||||||
for (const [k, v] of resp.headers)
|
for (const [k, v] of resp.headers) resp.headers.set(k, v);
|
||||||
resp.headers.set(k, v);
|
|
||||||
resp.body!.pipe(res);
|
resp.body!.pipe(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
flake.lock
generated
43
flake.lock
generated
@@ -1,43 +0,0 @@
|
|||||||
{
|
|
||||||
"nodes": {
|
|
||||||
"flake-utils": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1659877975,
|
|
||||||
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1665573177,
|
|
||||||
"narHash": "sha256-Arkrf3zmi3lXYpbSe9H+HQxswQ6jxsAmeQVq5Sr/OZc=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "d2afb051ffd904af5a825f58abee3c63b148c5f2",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "master",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"nixpkgs": "nixpkgs"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "root",
|
|
||||||
"version": 7
|
|
||||||
}
|
|
||||||
31
flake.nix
31
flake.nix
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
description = "A prisma test project";
|
|
||||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/master";
|
|
||||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
|
||||||
|
|
||||||
outputs = { self, nixpkgs, flake-utils }:
|
|
||||||
flake-utils.lib.eachDefaultSystem (system: let
|
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
|
||||||
in {
|
|
||||||
devShell = pkgs.mkShell {
|
|
||||||
nativeBuildInputs = [ pkgs.bashInteractive ];
|
|
||||||
buildInputs = with pkgs; [
|
|
||||||
nodePackages.prisma
|
|
||||||
nodePackages."@nestjs/cli"
|
|
||||||
nodePackages.npm
|
|
||||||
nodejs-slim
|
|
||||||
yarn
|
|
||||||
python3
|
|
||||||
pkg-config
|
|
||||||
];
|
|
||||||
shellHook = with pkgs; ''
|
|
||||||
export PRISMA_MIGRATION_ENGINE_BINARY="${prisma-engines}/bin/migration-engine"
|
|
||||||
export PRISMA_QUERY_ENGINE_BINARY="${prisma-engines}/bin/query-engine"
|
|
||||||
export PRISMA_QUERY_ENGINE_LIBRARY="${prisma-engines}/lib/libquery_engine.node"
|
|
||||||
export PRISMA_INTROSPECTION_ENGINE_BINARY="${prisma-engines}/bin/introspection-engine"
|
|
||||||
export PRISMA_FMT_BINARY="${prisma-engines}/bin/prisma-fmt"
|
|
||||||
export DATABASE_URL=postgresql://user:eip@localhost:5432/chromacase
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -29,6 +29,7 @@ import { unsetAccessToken } from './state/UserSlice';
|
|||||||
import TextButton from './components/TextButton';
|
import TextButton from './components/TextButton';
|
||||||
import ErrorView from './views/ErrorView';
|
import ErrorView from './views/ErrorView';
|
||||||
import GoogleView from './views/GoogleView';
|
import GoogleView from './views/GoogleView';
|
||||||
|
import VerifiedView from './views/VerifiedView';
|
||||||
|
|
||||||
// Util function to hide route props in URL
|
// Util function to hide route props in URL
|
||||||
const removeMe = () => '';
|
const removeMe = () => '';
|
||||||
@@ -75,6 +76,11 @@ const protectedRoutes = () =>
|
|||||||
link: undefined,
|
link: undefined,
|
||||||
},
|
},
|
||||||
User: { component: ProfileView, options: { title: translate('user') }, link: '/user' },
|
User: { component: ProfileView, options: { title: translate('user') }, link: '/user' },
|
||||||
|
Verified: {
|
||||||
|
component: VerifiedView,
|
||||||
|
options: { title: 'Verify email', headerShown: false },
|
||||||
|
link: '/verify',
|
||||||
|
},
|
||||||
} as const);
|
} as const);
|
||||||
|
|
||||||
const publicRoutes = () =>
|
const publicRoutes = () =>
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ export const en = {
|
|||||||
noRecentSearches: 'No recent searches',
|
noRecentSearches: 'No recent searches',
|
||||||
avatar: 'Avatar',
|
avatar: 'Avatar',
|
||||||
changeIt: 'Change It',
|
changeIt: 'Change It',
|
||||||
|
verified: "Verified",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fr: typeof en = {
|
export const fr: typeof en = {
|
||||||
@@ -366,6 +367,7 @@ export const fr: typeof en = {
|
|||||||
noRecentSearches: 'Aucune recherche récente',
|
noRecentSearches: 'Aucune recherche récente',
|
||||||
avatar: 'Avatar',
|
avatar: 'Avatar',
|
||||||
changeIt: 'Modifier',
|
changeIt: 'Modifier',
|
||||||
|
verified: "Verifié",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sp: typeof en = {
|
export const sp: typeof en = {
|
||||||
@@ -555,4 +557,5 @@ export const sp: typeof en = {
|
|||||||
|
|
||||||
avatar: 'Avatar',
|
avatar: 'Avatar',
|
||||||
changeIt: 'Cambialo',
|
changeIt: 'Cambialo',
|
||||||
|
verified: "Verified"
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export const UserValidator = yup
|
|||||||
username: yup.string().required(),
|
username: yup.string().required(),
|
||||||
password: yup.string().required().nullable(),
|
password: yup.string().required().nullable(),
|
||||||
email: yup.string().required(),
|
email: yup.string().required(),
|
||||||
|
emailVerified: yup.boolean().required(),
|
||||||
googleID: yup.string().required().nullable(),
|
googleID: yup.string().required().nullable(),
|
||||||
isGuest: yup.boolean().required(),
|
isGuest: yup.boolean().required(),
|
||||||
partyPlayed: yup.number().required(),
|
partyPlayed: yup.number().required(),
|
||||||
@@ -32,6 +33,7 @@ export const UserHandler: ResponseHandler<yup.InferType<typeof UserValidator>, U
|
|||||||
interface User extends Model {
|
interface User extends Model {
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
|
emailVerified: boolean;
|
||||||
googleID: string | null;
|
googleID: string | null;
|
||||||
isGuest: boolean;
|
isGuest: boolean;
|
||||||
premium: boolean;
|
premium: boolean;
|
||||||
|
|||||||
35
front/views/VerifiedView.tsx
Normal file
35
front/views/VerifiedView.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import API from '../API';
|
||||||
|
import { Text } from 'native-base';
|
||||||
|
import { useNavigation } from '../Navigation';
|
||||||
|
import { useRoute } from '@react-navigation/native';
|
||||||
|
|
||||||
|
const VerifiedView = () => {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
const route = useRoute();
|
||||||
|
const [failed, setFailed] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function run() {
|
||||||
|
try {
|
||||||
|
await API.fetch({
|
||||||
|
route: `/auth/verify?token=${(route.params as any).token}`,
|
||||||
|
method: 'PUT',
|
||||||
|
});
|
||||||
|
navigation.navigate('Home');
|
||||||
|
} catch {
|
||||||
|
setFailed(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return failed ? (
|
||||||
|
<Text>Email verification failed. The token has expired or is invalid.</Text>
|
||||||
|
) : (
|
||||||
|
<Text>Loading please wait</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VerifiedView;
|
||||||
@@ -55,6 +55,16 @@ const ProfileSettings = ({ navigation }: { navigation: any }) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
title: translate('verified'),
|
||||||
|
data: {
|
||||||
|
text: user.emailVerified ? 'verified' : 'not verified',
|
||||||
|
onPress: user.emailVerified
|
||||||
|
? undefined
|
||||||
|
: () => API.fetch({ route: '/auth/reverify', method: 'PUT' }),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
title: translate('avatar'),
|
title: translate('avatar'),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM python:latest
|
FROM python:3.10
|
||||||
RUN wget -q -O /tmp/websocketd.zip \
|
RUN wget -q -O /tmp/websocketd.zip \
|
||||||
https://github.com/joewalnes/websocketd/releases/download/v0.4.1/websocketd-0.4.1-linux_amd64.zip \
|
https://github.com/joewalnes/websocketd/releases/download/v0.4.1/websocketd-0.4.1-linux_amd64.zip \
|
||||||
&& unzip /tmp/websocketd.zip -d /tmp/websocketd && mv /tmp/websocketd/websocketd /usr/bin \
|
&& unzip /tmp/websocketd.zip -d /tmp/websocketd && mv /tmp/websocketd/websocketd /usr/bin \
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM python:latest
|
FROM python:3.10
|
||||||
RUN wget -q -O /tmp/websocketd.zip \
|
RUN wget -q -O /tmp/websocketd.zip \
|
||||||
https://github.com/joewalnes/websocketd/releases/download/v0.4.1/websocketd-0.4.1-linux_amd64.zip \
|
https://github.com/joewalnes/websocketd/releases/download/v0.4.1/websocketd-0.4.1-linux_amd64.zip \
|
||||||
&& unzip /tmp/websocketd.zip -d /tmp/websocketd && mv /tmp/websocketd/websocketd /usr/bin \
|
&& unzip /tmp/websocketd.zip -d /tmp/websocketd && mv /tmp/websocketd/websocketd /usr/bin \
|
||||||
|
|||||||
21
shell.nix
Normal file
21
shell.nix
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{pkgs ? import <nixpkgs> {}}:
|
||||||
|
pkgs.mkShell {
|
||||||
|
nativeBuildInputs = [pkgs.bashInteractive];
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
nodePackages.prisma
|
||||||
|
nodePackages."@nestjs/cli"
|
||||||
|
nodePackages.npm
|
||||||
|
nodejs_16
|
||||||
|
yarn
|
||||||
|
python3
|
||||||
|
pkg-config
|
||||||
|
];
|
||||||
|
shellHook = with pkgs; ''
|
||||||
|
# export PRISMA_MIGRATION_ENGINE_BINARY="${prisma-engines}/bin/migration-engine"
|
||||||
|
# export PRISMA_QUERY_ENGINE_BINARY="${prisma-engines}/bin/query-engine"
|
||||||
|
export PRISMA_QUERY_ENGINE_LIBRARY="${prisma-engines}/lib/libquery_engine.node"
|
||||||
|
export PRISMA_INTROSPECTION_ENGINE_BINARY="${prisma-engines}/bin/introspection-engine"
|
||||||
|
export PRISMA_FMT_BINARY="${prisma-engines}/bin/prisma-fmt"
|
||||||
|
export DATABASE_URL=postgresql://user:eip@localhost:5432/chromacase
|
||||||
|
'';
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user