mirror of
https://github.com/usatiuk/photos.git
synced 2025-10-28 07:27:47 +01:00
init
This commit is contained in:
45
.eslintrc.json
Normal file
45
.eslintrc.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"prettier",
|
||||
"import"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"prettier/@typescript-eslint",
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings",
|
||||
"plugin:import/typescript"
|
||||
],
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"settings": {
|
||||
"import/parsers": {
|
||||
"@typescript-eslint/parser": [
|
||||
".ts",
|
||||
".tsx"
|
||||
]
|
||||
},
|
||||
"import/resolver": {
|
||||
"typescript": {
|
||||
"alwaysTryTypes": true,
|
||||
"project": [
|
||||
"./tsconfig.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/require-await": "warn"
|
||||
}
|
||||
}
|
||||
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
.idea/
|
||||
node_modules/
|
||||
build/
|
||||
tmp/
|
||||
temp/
|
||||
dist/
|
||||
ormconfig.json
|
||||
.env
|
||||
.directory
|
||||
.history
|
||||
5
.prettierrc
Normal file
5
.prettierrc
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"trailingComma": "all",
|
||||
"tabWidth": 4,
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
8
.vscode/settings.json
vendored
Normal file
8
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"typeorm"
|
||||
],
|
||||
"files.exclude": {
|
||||
"frontend": true
|
||||
}
|
||||
}
|
||||
38
frontend/.eslintrc.json
Normal file
38
frontend/.eslintrc.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"plugins": [
|
||||
"jest",
|
||||
"react",
|
||||
"react-hooks",
|
||||
"html"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:jest/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:react/recommended"
|
||||
],
|
||||
"env": {
|
||||
"browser": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"settings": {
|
||||
"import/parsers": {
|
||||
"@typescript-eslint/parser": [
|
||||
".ts",
|
||||
".tsx"
|
||||
]
|
||||
},
|
||||
"import/resolver": {
|
||||
"typescript": {
|
||||
"alwaysTryTypes": true,
|
||||
"project": [
|
||||
"./tsconfig.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
frontend/.gitignore
vendored
Normal file
13
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
.idea/
|
||||
node_modules/
|
||||
build/
|
||||
tmp/
|
||||
temp/
|
||||
dist/
|
||||
ormconfig.json
|
||||
ormconfig.test.json
|
||||
.env
|
||||
.cache
|
||||
.directory
|
||||
.history
|
||||
.parcel-cache
|
||||
13278
frontend/package-lock.json
generated
Normal file
13278
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
42
frontend/package.json
Normal file
42
frontend/package.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "photos-frontend",
|
||||
"scripts": {
|
||||
"start": "parcel serve src/index.html",
|
||||
"build": "parcel build src/index.html",
|
||||
"lint": "eslint ./src/** --ext .js,.jsx,.ts,.tsx",
|
||||
"test": "jest"
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
"autoprefixer": true
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^4.4.0",
|
||||
"@typescript-eslint/parser": "^4.4.0",
|
||||
"eslint": "^7.10.0",
|
||||
"eslint-config-prettier": "^6.12.0",
|
||||
"eslint-import-resolver-typescript": "^2.3.0",
|
||||
"eslint-plugin-html": "^6.1.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-jest": "^24.1.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.3.1",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"eslint-plugin-react": "^7.21.3",
|
||||
"eslint-plugin-react-hooks": "^4.1.2",
|
||||
"jest": "^26.5.2",
|
||||
"parcel": "^2.0.0-nightly.419",
|
||||
"prettier": "^2.1.2",
|
||||
"prettier-eslint": "^11.0.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/eslint": "^7.2.4",
|
||||
"@types/eslint-plugin-prettier": "^3.1.0",
|
||||
"@types/prettier": "^2.1.1",
|
||||
"@types/react": "^16.9.51",
|
||||
"@types/react-dom": "^16.9.8"
|
||||
}
|
||||
}
|
||||
14
frontend/src/index.html
Normal file
14
frontend/src/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>My Parcel Project</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="./index.tsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
4
frontend/src/index.tsx
Normal file
4
frontend/src/index.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
import * as React from "react";
|
||||
import { render } from "react-dom";
|
||||
|
||||
render(<h1>Hello World</h1>, document.getElementById("root"));
|
||||
24
frontend/tsconfig.json
Normal file
24
frontend/tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"es2017",
|
||||
"dom"
|
||||
],
|
||||
"jsx": "react",
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "./dist",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*.ts",
|
||||
"./src/**/*.tsx"
|
||||
],
|
||||
}
|
||||
25
ormconfig.ci.json
Normal file
25
ormconfig.ci.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"type": "mariadb",
|
||||
"host": "localhost",
|
||||
"port": 3306,
|
||||
"username": "photos",
|
||||
"password": "photos",
|
||||
"database": "photos_test",
|
||||
"synchronize": true,
|
||||
"logging": false,
|
||||
"entities": [
|
||||
"src/entity/**/*.ts"
|
||||
],
|
||||
"migrations": [
|
||||
"src/migration/**/*.ts"
|
||||
],
|
||||
"subscribers": [
|
||||
"src/subscriber/**/*.ts"
|
||||
],
|
||||
"cli": {
|
||||
"entitiesDir": "src/entity",
|
||||
"migrationsDir": "src/migration",
|
||||
"subscribersDir": "src/subscriber"
|
||||
},
|
||||
"charset": "utf8mb4"
|
||||
}
|
||||
25
ormconfig.dev.json
Normal file
25
ormconfig.dev.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"type": "mariadb",
|
||||
"host": "db",
|
||||
"port": 3306,
|
||||
"username": "photos",
|
||||
"password": "photos",
|
||||
"database": "photos",
|
||||
"synchronize": true,
|
||||
"logging": false,
|
||||
"entities": [
|
||||
"src/entity/**/*.ts"
|
||||
],
|
||||
"migrations": [
|
||||
"src/migration/**/*.ts"
|
||||
],
|
||||
"subscribers": [
|
||||
"src/subscriber/**/*.ts"
|
||||
],
|
||||
"cli": {
|
||||
"entitiesDir": "src/entity",
|
||||
"migrationsDir": "src/migration",
|
||||
"subscribersDir": "src/subscriber"
|
||||
},
|
||||
"charset": "utf8mb4"
|
||||
}
|
||||
25
ormconfig.example.json
Normal file
25
ormconfig.example.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"type": "mariadb",
|
||||
"host": "localhost",
|
||||
"port": 3306,
|
||||
"username": "photos",
|
||||
"password": "photos",
|
||||
"database": "photos",
|
||||
"synchronize": true,
|
||||
"logging": false,
|
||||
"entities": [
|
||||
"src/entity/**/*.ts"
|
||||
],
|
||||
"migrations": [
|
||||
"src/migration/**/*.ts"
|
||||
],
|
||||
"subscribers": [
|
||||
"src/subscriber/**/*.ts"
|
||||
],
|
||||
"cli": {
|
||||
"entitiesDir": "src/entity",
|
||||
"migrationsDir": "src/migration",
|
||||
"subscribersDir": "src/subscriber"
|
||||
},
|
||||
"charset": "utf8mb4"
|
||||
}
|
||||
25
ormconfig.test.json
Normal file
25
ormconfig.test.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"type": "mariadb",
|
||||
"host": "dbtest",
|
||||
"port": 3306,
|
||||
"username": "photos",
|
||||
"password": "photos",
|
||||
"database": "photos_test",
|
||||
"synchronize": true,
|
||||
"logging": false,
|
||||
"entities": [
|
||||
"src/entity/**/*.ts"
|
||||
],
|
||||
"migrations": [
|
||||
"src/migration/**/*.ts"
|
||||
],
|
||||
"subscribers": [
|
||||
"src/subscriber/**/*.ts"
|
||||
],
|
||||
"cli": {
|
||||
"entitiesDir": "src/entity",
|
||||
"migrationsDir": "src/migration",
|
||||
"subscribersDir": "src/subscriber"
|
||||
},
|
||||
"charset": "utf8mb4"
|
||||
}
|
||||
4447
package-lock.json
generated
Normal file
4447
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
43
package.json
Normal file
43
package.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "photos",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"start-frontend": "cd frontend && npm start",
|
||||
"ts-node-dev": "ts-node-dev ./src/server.ts",
|
||||
"dev": "cross-env NODE_ENV=development concurrently npm:ts-node-dev npm:start-frontend -c 'blue,green'",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"lint": "eslint ./src/** --ext .js,.jsx,.ts,.tsx",
|
||||
"lint-frontend": "cd frontend && npm run lint",
|
||||
"lint-all": "npm run lint && npm run lint-frontend"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^4.4.0",
|
||||
"@typescript-eslint/parser": "^4.4.0",
|
||||
"bcrypt": "^5.0.0",
|
||||
"concurrently": "^5.3.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint": "^7.10.0",
|
||||
"eslint-config-prettier": "^6.12.0",
|
||||
"eslint-import-resolver-typescript": "^2.3.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"koa": "^2.13.0",
|
||||
"prettier": "^2.1.2",
|
||||
"prettier-eslint": "^11.0.0",
|
||||
"ts-node": "^9.0.0",
|
||||
"ts-node-dev": "^1.0.0-pre.63",
|
||||
"typeorm": "^0.2.28",
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^3.0.0",
|
||||
"@types/concurrently": "^5.2.1",
|
||||
"@types/eslint": "^7.2.3",
|
||||
"@types/eslint-plugin-prettier": "^3.1.0",
|
||||
"@types/jsonwebtoken": "^8.5.0",
|
||||
"@types/koa": "^2.11.4",
|
||||
"@types/prettier": "^2.1.1"
|
||||
}
|
||||
}
|
||||
11
photos.code-workspace
Normal file
11
photos.code-workspace
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"path": "frontend"
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
||||
7
src/app.ts
Normal file
7
src/app.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import * as Koa from "koa";
|
||||
|
||||
export const app = new Koa();
|
||||
|
||||
app.use(async (ctx) => {
|
||||
ctx.body = "hello!";
|
||||
});
|
||||
8
src/config/database.ts
Normal file
8
src/config/database.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import "../entity/User";
|
||||
|
||||
import { Connection, createConnection } from "typeorm";
|
||||
import { config } from "./";
|
||||
|
||||
export async function connect(): Promise<Connection> {
|
||||
return createConnection(config.dbConnectionOptions);
|
||||
}
|
||||
65
src/config/index.ts
Normal file
65
src/config/index.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import * as fs from "fs";
|
||||
import { ConnectionOptions } from "typeorm";
|
||||
import { sys } from "typescript";
|
||||
|
||||
export enum EnvType {
|
||||
production,
|
||||
development,
|
||||
test,
|
||||
}
|
||||
|
||||
export interface IConfig {
|
||||
env: EnvType;
|
||||
port: number;
|
||||
jwtSecret: string;
|
||||
dbConnectionOptions: ConnectionOptions | null;
|
||||
}
|
||||
|
||||
const production: IConfig = {
|
||||
env: EnvType.production,
|
||||
port: process.env.PORT ? parseInt(process.env.PORT, 10) : 3000,
|
||||
jwtSecret: ((): string => {
|
||||
if (process.env.JWT_SECRET === undefined) {
|
||||
console.log("JWT_SECRET is not set");
|
||||
process.exit(1);
|
||||
}
|
||||
return process.env.JWT_SECRET;
|
||||
})(),
|
||||
dbConnectionOptions: null,
|
||||
};
|
||||
|
||||
const development: IConfig = {
|
||||
...production,
|
||||
env: EnvType.development,
|
||||
jwtSecret: "DEVSECRET",
|
||||
dbConnectionOptions:
|
||||
process.env.NODE_ENV === "development"
|
||||
? fs.existsSync("./ormconfig.dev.json")
|
||||
? (JSON.parse(
|
||||
fs.readFileSync("./ormconfig.dev.json").toString(),
|
||||
) as ConnectionOptions)
|
||||
: null
|
||||
: null,
|
||||
};
|
||||
|
||||
const test: IConfig = {
|
||||
...production,
|
||||
env: EnvType.test,
|
||||
jwtSecret: "TESTSECRET",
|
||||
dbConnectionOptions:
|
||||
process.env.NODE_ENV === "test"
|
||||
? process.env.CI
|
||||
? (JSON.parse(
|
||||
fs.readFileSync("./ormconfig.ci.json").toString(),
|
||||
) as ConnectionOptions)
|
||||
: (JSON.parse(
|
||||
fs.readFileSync("./ormconfig.test.json").toString(),
|
||||
) as ConnectionOptions)
|
||||
: null,
|
||||
};
|
||||
|
||||
const envs: { [key: string]: IConfig } = { production, development, test };
|
||||
const env = process.env.NODE_ENV || "production";
|
||||
const currentConfig = envs[env];
|
||||
|
||||
export { currentConfig as config };
|
||||
66
src/entity/User.ts
Normal file
66
src/entity/User.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import * as bcrypt from "bcrypt";
|
||||
import * as jwt from "jsonwebtoken";
|
||||
import {
|
||||
BaseEntity,
|
||||
Column,
|
||||
Entity,
|
||||
Index,
|
||||
PrimaryGeneratedColumn,
|
||||
} from "typeorm";
|
||||
import { config } from "../config";
|
||||
|
||||
export type IUserJSON = Pick<User, "id" | "username">;
|
||||
|
||||
export interface IUserJWT extends IUserJSON {
|
||||
ext: number;
|
||||
iat: number;
|
||||
}
|
||||
|
||||
export interface IUserAuthJSON extends IUserJSON {
|
||||
jwt: string;
|
||||
}
|
||||
|
||||
@Entity()
|
||||
export class User extends BaseEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
public id: number;
|
||||
|
||||
@Column({ length: 190 })
|
||||
@Index({ unique: true })
|
||||
public username: string;
|
||||
|
||||
@Column({ length: 190 })
|
||||
@Index({ unique: true })
|
||||
public email: string;
|
||||
|
||||
@Column({ length: 190 })
|
||||
public passwordHash: string;
|
||||
|
||||
constructor(username: string, email: string) {
|
||||
super();
|
||||
this.username = username;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public async verifyPassword(password: string): Promise<boolean> {
|
||||
return bcrypt.compare(password, this.passwordHash);
|
||||
}
|
||||
|
||||
public async setPassword(password: string): Promise<void> {
|
||||
this.passwordHash = await bcrypt.hash(password, 10);
|
||||
}
|
||||
|
||||
public toJSON(): IUserJSON {
|
||||
const { id, username } = this;
|
||||
return { id, username };
|
||||
}
|
||||
|
||||
public toAuthJSON(): IUserAuthJSON {
|
||||
const { id, username } = this;
|
||||
return { id, username, jwt: this.toJWT() };
|
||||
}
|
||||
|
||||
public toJWT(): string {
|
||||
return jwt.sign(this.toJSON(), config.jwtSecret, { expiresIn: "31d" });
|
||||
}
|
||||
}
|
||||
11
src/server.ts
Normal file
11
src/server.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { app } from "./app";
|
||||
import { config } from "./config";
|
||||
import { connect } from "./config/database";
|
||||
|
||||
connect()
|
||||
.then((connection) => {
|
||||
console.log(`Connected to ${connection.name}`);
|
||||
app.listen(config.port);
|
||||
console.log(`Listening at ${config.port}`);
|
||||
})
|
||||
.catch((e) => console.log(e));
|
||||
23
tsconfig.json
Normal file
23
tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"es2017"
|
||||
],
|
||||
"target": "es2017",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "./dist",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*.ts",
|
||||
],
|
||||
"exclude": [
|
||||
"frontend"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user