Commit 1d7af9dc authored by 魏裕群's avatar 魏裕群

init commit

parent c2148d91
.git
\ No newline at end of file
logs/
npm-debug.log
node_modules/
coverage/
.idea/
run/
logs/
.DS_Store
.vscode
*.swp
*.lock
*.js
app/**/*.js
test/**/*.js
config/**/*.js
app/**/*.map
test/**/*.map
config/**/*.map
\ No newline at end of file
variables:
DOCKER_REGISTRY: 't-docker.51gjj.com'
before_script:
- docker login -u pengtianzi -p 4dc8b3a4d91fa7fa $DOCKER_REGISTRY
stages:
- pre_deploy
build_docker_image:
stage: pre_deploy
only:
- dev
- uat
- pro
- tags
script:
#- export IMAGE_TAG=${CI_COMMIT_TAG:-latest}
- docker build -t $DOCKER_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:$CI_BUILD_REF_NAME .
- docker push $DOCKER_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:$CI_BUILD_REF_NAME
tags:
- docker-image-builder
\ No newline at end of file
FROM t-docker.51gjj.com/tools/node:8.12.0
ENV LANG C.UTF-8
ENV NODE_ENV production
ENV PATH $PATH:/usr/local/node/bin/
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
RUN yarn config set registry "https://registry.npm.taobao.org/"
RUN yarn install
RUN yarn add typescript
RUN yarn add egg-ts-helper
COPY . /usr/src/app
EXPOSE 7001
CMD [ "yarn", "docker" ]
11111 # hackernews-async-ts
\ No newline at end of file
## QuickStart
### Development
```bash
$ npm i
$ npm run dev
$ open http://localhost:7001/
```
Don't tsc compile at development mode, if you had run `tsc` then you need to `npm run clean` before `npm run dev`.
### Deploy
```bash
$ npm start
```
### Npm Scripts
- Use `npm run lint` to check code style
- Use `npm test` to run unit test
- se `npm run clean` to clean compiled js at development mode once
### Requirement
- Node.js 8.x
- Typescript 2.8+
### Model eg:
```
module.exports = (app, model) => {
const { INTEGER, DATE } = app.Sequelize;
return model.define('#modelName#', {
id: {
type: INTEGER,
primaryKey: true,
autoIncrement: true,
field: 'id'
},
createdAt: {
type: DATE,
field: 'created_at'
},
updatedAt: {
type: DATE,
field: 'updated_at'
},
}, {
tableName: '#tableName#',
timestamps: true,
underscored: false,
});
};
```
'use strict';
export default app => {
app.beforeStart(async () => {
app.validator.addRule('intString', ({}, value) => {
if (typeof value === 'string' && /^\d+$/.test(value)) {
return;
}
return 'must be intString';
});
});
};
\ No newline at end of file
import { Controller } from 'egg';
import router from 'egg-router-decorator';
@router.prefix('/')
export default class HomeController extends Controller {
@router.get('')
public async index() {
this.ctx.body = 'hello word';
}
@router.get('mock_login/:id')
public async mockLogin() {
const { ctx } = this;
ctx.validate({
id: {
type: 'intString'
}
}, ctx.params);
const userId = +ctx.params.id;
const token = await ctx.service.jwt.genToken(userId);
this.ctx.body = { token };
}
}
\ No newline at end of file
import { Context } from 'egg';
import { get } from 'lodash';
export default {
get userId(this: Context): number | string | null{
const { app: { config: { jwt: options }} } = this;
const property = options.property || options.userProperty || "user";
const data = get(this.state, property);
return (data && data.userId) || null;
},
isAjax(this: Context) {
return this.get('X-Requested-With') === 'XMLHttpRequest';
},
failed(this: Context, message: string, code?: number) {
const error: any = new Error(message);
error.code = code || -1;
this.throw(error, 422);
}
}
\ No newline at end of file
'use strict';
import * as moment from 'moment';
export function relativeTime(time) {
return moment(new Date(time * 1000)).fromNow();
};
export function domain (url) {
return url && url.split('/')[2];
};
'use strict';
import * as crypto from 'crypto';
export function md5 (source: string) {
return crypto.createHash('md5').update(source).digest('hex');
};
\ No newline at end of file
import { Context, Application } from 'egg';
import { get } from 'lodash';
function match(expr, text) {
if (!/\*/.test(expr)) {
return text === expr;
}
const pattern = '^' + expr
.replace(/\//gi, '\\/')
.replace(/\*\*/gi, '(?:[a-zA-Z_0-9\-\/]+)')
.replace(/\*/gi, '(?:[a-zA-Z_0-9\-]+)') + '$';
return new RegExp(pattern).test(text);
}
// 这里是你自定义的中间件
export default function middleware(options: any, app: Application): any {
return async (ctx: Context, next: () => Promise<any>) => {
const { request } = ctx;
/**
* 路径找不到就不判断权限
*/
if (!app.router.match(request.path, request.method).route) {
await next();
return;
}
if (options.exclude) {
if (options.exclude.filter(item => match(`${app.config.router.prefix}${item}`, request.path)).length) {
app.logger.info(`[middleware-auth] ignore url ${request.url}`);
await next();
return;
}
}
const property = options.property || options.userProperty || "user";
const data = get(ctx.state, property);
if (!data || !data.userId) {
ctx.throw('Forbidden', 403);
}
await next();
};
}
\ No newline at end of file
import { Application } from 'egg';
import { initRouter } from 'egg-router-decorator';
export default (app: Application) => {
initRouter(app, app.config.router);
};
import { Service } from 'egg';
export class JwtSevice extends Service {
async genToken(userId: string | number) {
const { app } = this;
const token = app.jwt.sign({ userId }, app.config.jwt.secret);
return token;
}
}
export default JwtSevice;
\ No newline at end of file
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<link rel="stylesheet" href="/public/css/news.css" />
<link rel="icon" href="/public/favicon.png" type="image/x-icon">
<title>{% block title %}egg - HackerNews{% endblock %}</title>
</head>
<body>
<div id="wrapper">
<div id="header">
<a id="yc" href="http://www.ycombinator.com"><img src="https://news.ycombinator.com/y18.gif"></a>
<h1><a href="/news">Hacker News</a></h1>
<span class="source">
Built with <a href="https://eggjs.org/" target="_blank">Egg</a> | <a href="https://github.com/eggjs/examples/tree/master/hackernews-async-ts" target="_blank">Source</a>
</span>
</div>
{% block content %}{% endblock %}
</div>
</body>
</html>
{% extends "../layout/layout.tpl" %}
{% block content %}
<div class="item-view view v-transition">
<!-- item detail -->
{% include "./item.tpl" %}
<!-- comments -->
{% if comments.length > 0%}
<ul class="comments"
{% for comment in comments %}
<li>
<div class="comhead">
<a class="toggle">[-]</a>
<a href="/news/user/{{ comment.by }}">{{ comment.by }}</a>
{{ comment.time | relativeTime }}
</div>
<div class="comment-content">
{{ helper.shtml(comment.text) }}
</div>
</li>
{% endfor %}
</ul>
{% else %}
<p>No comments yet.</p>
{% endif %}
</div>
{% endblock %}
\ No newline at end of file
<div class="item">
<span class="index">{{ index }}.</span>
<p>
<a class="title" target="_blank" href="{{ item.url }}">{{ helper.shtml(item.title) }}</a>
<span class="domain">({{ item.url | domain }})</span>
</p>
<p class="subtext">
<span>
{{ item.score }} points by <a href="/news/user/{{ item.by }}">{{ item.by }}</a>
</span>
{{ item.time | relativeTime }}
<span class="comments-link">
| <a href="/news/item/{{ item.id }}">{{ item.descendants }} comments</a>
</span>
</p>
</div>
\ No newline at end of file
{% extends "../layout/layout.tpl" %}
{% block content %}
<div class="news-view view v-transition">
{% for item in list %}
{% set index = ((page-1) * pageSize + loop.index) %}
{% include "./item.tpl" %}
{% endfor %}
<div class="nav">
{% if page > 1 %}
<a href="/news?page={{ page - 1 }}">&lt; prev</a>
{% endif %}
<a href="/news?page={{ page + 1 }}">more...</a>
</div>
</div>
{% endblock %}
{% extends "../layout/layout.tpl" %}
{% block title %}
Profile: {{ user.id }} | egg - HackerNews
{% endblock %}
{% block content %}
<div class="user-view view v-transition">
<ul>
<li><span class="label">user:</span> {{ user.id }}</li>
<li><span class="label">created:</span> {{ user.created | relativeTime }}</li>
<li><span class="label">karma:</span> {{ user.karma }}</li>
<li>
<span class="label">about:</span>
<div class="about">
{{ helper.shtml(user.about) }}
</div>
</li>
</ul>
<p class="links">
<a href="https://news.ycombinator.com/submitted?id={{ user.id }}">submissions</a><br>
<a href="https://news.ycombinator.com/threads?id={{ user.id }}">comments</a>
</p>
</div>
{% endblock %}
\ No newline at end of file
'use strict';
import { EggAppConfig, PowerPartial } from 'egg';
import * as fs from 'fs';
import * as path from 'path';
// for config.{env}.ts
export type DefaultConfig = PowerPartial<EggAppConfig & MyAppConfig>;
// app special config scheme
export interface MyAppConfig {
router: {
prefix: string
}
}
export default (appInfo: EggAppConfig) => {
const config = {} as PowerPartial<EggAppConfig> & MyAppConfig;
config.security = {
csrf: {
enable: false,
},
domainWhiteList: [],
};
config.router = {
prefix: ''
};
config.middleware = [
'auth',
];
config.auth = {
exclude: [
'/',
'/mock_login/*'
],
};
// override config from framework / plugin
config.keys = appInfo.name + '_1537508791174_5973';
config.sessionExpireIn = 7 * 3600 * 24;
config.jwt = {
secret: appInfo.name + '123456',
enable: true,
credentialsRequired: false,
exp: Math.floor(Date.now() / 1000) + config.sessionExpireIn, // 一周
getToken(ctx) {
const parts = ctx.get('authorization').split(' ');
if (parts.length === 2) {
const scheme = parts[0];
const credentials = parts[1];
if (/^Bearer$/i.test(scheme)) {
return credentials;
}
}
const { session_token: token } = ctx.request.query;
return token;
},
};
config.onerror = {
appErrorFilter: err => {
if (err.status && Math.floor(err.status / 100) === 4) {
return false;
}
return true;
},
accepts: () => 'json',
};
config.view = {
defaultViewEngine: 'nunjucks',
mapping: {
'.tpl': 'nunjucks',
},
};
config.siteFile = {
'/favicon.ico': fs.readFileSync(path.join(appInfo.baseDir, 'app/public/favicon.png')),
};
return config;
};
import { DefaultConfig } from './config.default';
export default () => {
const config: DefaultConfig = {};
config.sequelize = {
datasources: [
// {
// timezone: '+08:00',
// dialect: 'mysql',
// delegate: 'model',
// baseDir: 'model',
// host: '',
// port: 3306,
// database: '',
// username: '',
// password: '',
// }
]
};
config.redis = {
// client: {
// port: 6379,
// host: '',
// password: '',
// db: 0,
// },
}
return config;
};
import { DefaultConfig } from './config.default';
import { EggAppConfig } from 'egg';
export default (appInfo: EggAppConfig) => {
const config: DefaultConfig = {};
config.keys = appInfo.name + '_1557111601312_6918';
config.jwt = {
secret: appInfo.name + '123456',
}
config.sequelize = {
datasources: [
// {
// timezone: '+08:00',
// dialect: 'mysql',
// delegate: 'model',
// baseDir: 'model',
// port: 3306,
// username: process.env.MYSQL_USER,
// host: process.env.MYSQL_HOST,
// database: process.env.MYSQL_DB_NAME,
// password: process.env.MYSQL_PWD,
// }
]
};
config.redis = {
// client: {
// port: 6379,
// host: process.env.REDIS_HOST,
// password: process.env.REDIS_PWD,
// db: 0,
// },
}
return config;
};
'use strict';
export default {
nunjucks: {
enable: true,
package: 'egg-view-nunjucks',
},
jwt: {
enable: true,
package: "egg-jwt"
},
onerror: {
enable: true,
package: 'egg-onerror',
},
sequelize: {
enable: true,
package: 'egg-sequelize',
},
redis: {
enable: true,
package: 'egg-redis',
},
validate: {
enable: true,
package: 'egg-validate',
}
};
declare module 'egg' {
interface EggAppConfig {
jwt: {
secret: string;
enable?: boolean;
sign?: SignOptions;
verify?: VerifyOptions;
property?: string;
userProperty?: string;
credentialsRequired?: boolean;
exp?: number;
getToken?: Function;
};
}
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "@jianbing/egg-ts",
"version": "1.0.0",
"description": "",
"egg": {
"typescript": true
},
"scripts": {
"start": "npm run tsc && egg-scripts start",
"dev": "egg-bin dev -r egg-ts-helper/register",
"debug": "egg-bin debug -r egg-ts-helper/register",
"docker": "npm run tsc && eggctl start --title=",
"test-local": "egg-bin test -r egg-ts-helper/register",
"test": "npm run lint -- --fix && npm run test-local",
"cov": "egg-bin cov -r egg-ts-helper/register",
"tsc": "ets && tsc -p tsconfig.json",
"ci": "npm run lint && npm run cov && npm run tsc",
"autod": "autod",
"lint": "tslint .",
"clean": "ets clean"
},
"dependencies": {
"egg": "^2.6.0",
"egg-jwt": "^3.1.6",
"egg-redis": "^2.3.1",
"egg-router-decorator": "^2.1.0",
"egg-scripts": "^2.6.0",
"egg-sequelize": "^4.3.1",
"egg-validate": "^2.0.2",
"egg-view-nunjucks": "^2.2.0",
"moment": "^2.22.0",
"mysql2": "^1.6.5"
},
"devDependencies": {
"@types/cheerio": "^0.22.1",
"@types/mocha": "^2.2.40",
"@types/node": "^7.0.12",
"@types/supertest": "^2.0.0",
"autod": "^3.0.1",
"autod-egg": "^1.1.0",
"cheerio": "^1.0.0-rc.2",
"egg-bin": "^4.6.2",
"egg-mock": "^3.16.0",
"egg-ts-helper": "^1.4.2",
"tslib": "^1.9.0",
"tslint": "^4.0.0",
"typescript": "^2.8.1"
},
"engines": {
"node": ">=8.9.0"
}
}
{
"compileOnSave": true,
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"strict": true,
"noImplicitAny": false,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"charset": "utf8",
"allowJs": false,
"pretty": true,
"noEmitOnError": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"strictPropertyInitialization": false,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"inlineSourceMap": true,
"importHelpers": true
},
"exclude": [
"app/public",
"app/views"
]
}
{
"extends": "tslint:latest",
"rules": {
"quotemark": [
true,
"single",
"jsx-double"
],
"no-console": [
true,
"dir",
"log",
"error",
"warn"
],
"space-before-function-paren": false,
"interface-name": [
true,
"never-prefix"
],
"adjacent-overload-signatures": true,
"member-access": [
false
],
"member-ordering": [
true,
{
"order": "fields-first"
}
],
"object-literal-sort-keys": false,
"max-classes-per-file": [
true,
10
],
"variable-name": [
true,
"allow-leading-underscore"
],
"align": [true, "statements"]
}
}
// This file is created by egg-ts-helper@1.25.3
// Do not modify this file!!!!!!!!!
import 'egg';
import ExportHome from '../../../app/controller/home';
declare module 'egg' {
interface IController {
home: ExportHome;
}
}
// This file is created by egg-ts-helper@1.25.3
// Do not modify this file!!!!!!!!!
import 'egg';
import ExtendContext from '../../../app/extend/context';
declare module 'egg' {
type ExtendContextType = typeof ExtendContext;
interface Context extends ExtendContextType { }
}
\ No newline at end of file
// This file is created by egg-ts-helper@1.25.3
// Do not modify this file!!!!!!!!!
import 'egg';
import ExtendIHelper from '../../../app/extend/helper';
declare module 'egg' {
type ExtendIHelperType = typeof ExtendIHelper;
interface IHelper extends ExtendIHelperType { }
}
\ No newline at end of file
// This file is created by egg-ts-helper@1.25.3
// Do not modify this file!!!!!!!!!
import 'egg';
export * from 'egg';
export as namespace Egg;
// This file is created by egg-ts-helper@1.25.3
// Do not modify this file!!!!!!!!!
import 'egg';
import ExportAuth from '../../../app/middleware/auth';
declare module 'egg' {
interface IMiddleware {
auth: typeof ExportAuth;
}
}
// This file is created by egg-ts-helper@1.25.3
// Do not modify this file!!!!!!!!!
import 'egg';
import ExportJwt from '../../../app/service/Jwt';
declare module 'egg' {
interface IService {
jwt: ExportJwt;
}
}
// This file is created by egg-ts-helper@1.25.3
// Do not modify this file!!!!!!!!!
import 'egg';
import { EggAppConfig } from 'egg';
import ExportConfigDefault from '../../config/config.default';
type ConfigDefault = ReturnType<typeof ExportConfigDefault>;
declare module 'egg' {
type NewEggAppConfig = ConfigDefault;
interface EggAppConfig extends NewEggAppConfig { }
}
\ No newline at end of file
// This file is created by egg-ts-helper@1.25.3
// Do not modify this file!!!!!!!!!
import 'egg';
import 'egg-onerror';
import 'egg-session';
import 'egg-i18n';
import 'egg-watcher';
import 'egg-multipart';
import 'egg-security';
import 'egg-development';
import 'egg-logrotator';
import 'egg-schedule';
import 'egg-static';
import 'egg-jsonp';
import 'egg-view';
import 'egg-view-nunjucks';
import 'egg-jwt';
import 'egg-sequelize';
import 'egg-redis';
import 'egg-validate';
import { EggPluginItem } from 'egg';
declare module 'egg' {
interface EggPlugin {
onerror?: EggPluginItem;
session?: EggPluginItem;
i18n?: EggPluginItem;
watcher?: EggPluginItem;
multipart?: EggPluginItem;
security?: EggPluginItem;
development?: EggPluginItem;
logrotator?: EggPluginItem;
schedule?: EggPluginItem;
static?: EggPluginItem;
jsonp?: EggPluginItem;
view?: EggPluginItem;
nunjucks?: EggPluginItem;
jwt?: EggPluginItem;
sequelize?: EggPluginItem;
redis?: EggPluginItem;
validate?: EggPluginItem;
}
}
\ No newline at end of file
declare module 'egg' {
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment