├── frontend
├── .gitkeep
├── babel.config.js
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── assets
│ │ ├── fullstack.png
│ │ ├── tencent-cloud-hero.gif
│ │ └── fonts
│ │ │ ├── serverless-regular.woff
│ │ │ └── serverless-regular.woff2
│ ├── main.js
│ ├── components
│ │ ├── UserList.vue
│ │ └── UserForm.vue
│ ├── config.js
│ ├── style
│ │ └── app.css
│ └── App.vue
├── vue.config.js
├── README.md
├── serverless.yml
└── package.json
├── .env.example
├── api
├── package.json
├── serverless.yml
├── sls.js
└── controller
│ └── user.js
├── vpc
└── serverless.yml
├── db
└── serverless.yml
├── .gitignore
├── serverless.yml
├── package.json
├── scripts
└── bootstrap.js
├── LICENSE
├── README.md
└── README.en.md
/frontend/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless-components/tencent-fullstack/HEAD/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/src/assets/fullstack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless-components/tencent-fullstack/HEAD/frontend/src/assets/fullstack.png
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # secret for credential
2 | TENCENT_SECRET_ID=xxx
3 | TENCENT_SECRET_KEY=xxx
4 |
5 | # global config
6 | REGION=ap-guangzhou
7 | ZONE=ap-guangzhou-2
--------------------------------------------------------------------------------
/frontend/src/assets/tencent-cloud-hero.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless-components/tencent-fullstack/HEAD/frontend/src/assets/tencent-cloud-hero.gif
--------------------------------------------------------------------------------
/frontend/src/assets/fonts/serverless-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless-components/tencent-fullstack/HEAD/frontend/src/assets/fonts/serverless-regular.woff
--------------------------------------------------------------------------------
/frontend/src/assets/fonts/serverless-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serverless-components/tencent-fullstack/HEAD/frontend/src/assets/fonts/serverless-regular.woff2
--------------------------------------------------------------------------------
/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import App from './App.vue';
3 | // import 'env';
4 | import './style/app.css';
5 |
6 | Vue.config.productionTip = false;
7 |
8 | new Vue({
9 | render: (h) => h(App),
10 | }).$mount('#app');
11 |
--------------------------------------------------------------------------------
/frontend/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | configureWebpack: {
3 | resolve: {
4 | alias: {
5 | ENV: require('path').resolve(__dirname, 'env.js'),
6 | },
7 | },
8 | externals: {
9 | env: 'env'
10 | },
11 | plugins: [],
12 | },
13 | };
14 |
--------------------------------------------------------------------------------
/api/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@serverless-fullstack/api",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "body-parser": "^1.19.0",
6 | "cors": "^2.8.5",
7 | "dotenv": "^8.2.0",
8 | "express": "^4.17.1",
9 | "pg": "^7.14.0"
10 | },
11 | "license": "MIT"
12 | }
13 |
--------------------------------------------------------------------------------
/vpc/serverless.yml:
--------------------------------------------------------------------------------
1 | org: fullstack
2 | app: fullstack-serverless-db
3 | stage: dev
4 |
5 | component: vpc # (required) name of the component. In that case, it's vpc.
6 | name: fullstack-vpc # (required) name of your vpc component instance.
7 |
8 | inputs:
9 | region: ${env:REGION}
10 | zone: ${env:ZONE}
11 | vpcName: serverless
12 | subnetName: serverless
13 |
--------------------------------------------------------------------------------
/db/serverless.yml:
--------------------------------------------------------------------------------
1 | org: fullstack
2 | app: fullstack-serverless-db
3 | stage: dev
4 |
5 | component: postgresql
6 | name: fullstack-db
7 |
8 | inputs:
9 | region: ${env:REGION}
10 | zone: ${env:ZONE}
11 | dBInstanceName: ${name}
12 | vpcConfig:
13 | vpcId: ${output:${stage}:${app}:fullstack-vpc.vpcId}
14 | subnetId: ${output:${stage}:${app}:fullstack-vpc.subnetId}
15 | extranetAccess: false
16 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # frontend
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Lints and fixes files
19 | ```
20 | npm run lint
21 | ```
22 |
23 | ### Customize configuration
24 | See [Configuration Reference](https://cli.vuejs.org/config/).
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.sublime-project
3 | *.sublime-workspace
4 | *.log
5 | .serverless
6 | v8-compile-cache-*
7 | jest/*
8 | coverage
9 | testProjects/*/package-lock.json
10 | testProjects/*/yarn.lock
11 | .serverlessUnzipped
12 | node_modules
13 | .vscode/
14 | .eslintcache
15 | dist
16 | .idea
17 | build/
18 | .env
19 | .cache*
20 | .serverless
21 | .serverless_nextjs
22 | .serverless_plugins
23 | env.js
24 | tmp
25 | package-lock.json
26 | yarn.lock
27 | test
28 | .npmrc
--------------------------------------------------------------------------------
/serverless.yml:
--------------------------------------------------------------------------------
1 | name: fullstack
2 | author: Tencent Cloud, Inc.
3 | org: Tencent Cloud, Inc.
4 | description: Deploy a full stack application.
5 | keywords: tencent, serverless, express, website, fullstack
6 | repo: https://github.com/serverless-components/tencent-fullstack
7 | readme: https://github.com/serverless-components/tencent-fullstack/tree/master/README.md
8 | license: MIT
9 | src:
10 | src: ./
11 | exclude:
12 | - .env
13 | - serverless.yml
14 | - '**/node_modules'
15 | - '**/package-lock.json'
16 |
--------------------------------------------------------------------------------
/frontend/serverless.yml:
--------------------------------------------------------------------------------
1 | org: fullstack
2 | app: fullstack-serverless-db
3 | stage: dev
4 | component: website
5 | name: fullstack-frontend
6 |
7 | inputs:
8 | region: ${env:REGION}
9 | bucketName: fullstack-serverless-db
10 | protocol: https
11 | src:
12 | src: ./
13 | hook: npm run build
14 | dist: ./dist
15 | envPath: ./
16 | index: index.html
17 | error: index.html
18 | env:
19 | # get api url after below api service deployed.
20 | apiUrl: ${output:${stage}:${app}:fullstack-api.apigw.url}
21 |
--------------------------------------------------------------------------------
/api/serverless.yml:
--------------------------------------------------------------------------------
1 | org: fullstack
2 | app: fullstack-serverless-db
3 | stage: dev
4 | component: express
5 | name: fullstack-api
6 |
7 | inputs:
8 | src:
9 | src: ./
10 | exclude:
11 | - .env
12 | functionName: ${name}
13 | region: ${env:REGION}
14 | runtime: Nodejs10.15
15 | functionConf:
16 | timeout: 30
17 | vpcConfig:
18 | vpcId: ${output:${stage}:${app}:fullstack-vpc.vpcId}
19 | subnetId: ${output:${stage}:${app}:fullstack-vpc.subnetId}
20 | environment:
21 | variables:
22 | PG_CONNECT_STRING: ${output:${stage}:${app}:fullstack-db.private.connectionString}
23 | apigatewayConf:
24 | enableCORS: true
25 | protocols:
26 | - http
27 | - https
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tencent-fullstack",
3 | "version": "1.1.0",
4 | "description": "Fullstack with serverless db",
5 | "main": "index.js",
6 | "scripts": {
7 | "bootstrap": "node scripts/bootstrap.js",
8 | "deploy": "sls deploy --all",
9 | "remove": "sls remove --all",
10 | "info": "npm run info:vpc && npm run info:db && npm run info:api && npm run info:frontend",
11 | "info:vpc": "sls info --target=./vpc",
12 | "info:db": "sls info --target=./db",
13 | "info:api": "sls info --target=./api",
14 | "info:frontend": "sls info --target=./frontend"
15 | },
16 | "keywords": [
17 | "fullstack",
18 | "serverless",
19 | "serverless-db"
20 | ],
21 | "author": "yugasun",
22 | "license": "MIT"
23 | }
24 |
--------------------------------------------------------------------------------
/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 | 基于 Serverless Framework 的秒杀H5
14 |
15 |
16 |
17 |
18 | We're sorry but frontend doesn't work properly without JavaScript
20 | enabled. Please enable it to continue.
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/scripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const path = require('path')
4 | const util = require('util')
5 | const exec = util.promisify(require('child_process').exec)
6 |
7 | const rootDir = path.join(__dirname, '..')
8 | const apiDir = path.join(rootDir, 'api')
9 | const frontendDir = path.join(rootDir, 'frontend')
10 |
11 | async function installDependencies(dir) {
12 | await exec('npm install', {
13 | cwd: dir
14 | })
15 | }
16 |
17 | /* eslint-disable no-console*/
18 | async function bootstrap() {
19 | console.log('Start install dependencies...\n')
20 | console.log('Start install api dependencies...\n')
21 | await installDependencies(apiDir)
22 | console.log('Api dependencies installed success.')
23 | console.log('Start install frontend dependencies...\n')
24 | await installDependencies(frontendDir)
25 | console.log('Frontend dependencies installed success.')
26 | console.log('All dependencies installed.')
27 | }
28 |
29 | bootstrap()
30 |
31 | process.on('unhandledRejection', (e) => {
32 | throw e
33 | })
34 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "axios": "^0.19.0",
12 | "core-js": "^3.4.3",
13 | "vue": "^2.6.10"
14 | },
15 | "devDependencies": {
16 | "@vue/cli-plugin-babel": "^4.1.0",
17 | "@vue/cli-plugin-eslint": "^4.1.0",
18 | "@vue/cli-service": "^4.1.0",
19 | "babel-eslint": "^10.0.3",
20 | "eslint": "^5.16.0",
21 | "eslint-plugin-vue": "^5.0.0",
22 | "vue-template-compiler": "^2.6.10"
23 | },
24 | "eslintConfig": {
25 | "root": true,
26 | "env": {
27 | "node": true
28 | },
29 | "extends": [
30 | "plugin:vue/essential",
31 | "eslint:recommended"
32 | ],
33 | "rules": {},
34 | "parserOptions": {
35 | "parser": "babel-eslint"
36 | }
37 | },
38 | "browserslist": [
39 | "> 1%",
40 | "last 2 versions"
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Yuga Sun
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/frontend/src/components/UserList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
User List
4 |
5 |
6 |
19 | {{ loading ? 'Loading' : 'No Data'}}
20 |
21 |
22 |
23 |
45 |
54 |
--------------------------------------------------------------------------------
/frontend/src/config.js:
--------------------------------------------------------------------------------
1 | import image0 from './assets/p1.jpg';
2 | import image1 from './assets/p2.jpg';
3 |
4 | const defaultPrizes = [
5 | {
6 | prizeId: '1',
7 | start: '09:00',
8 | title: '腾讯云公仔',
9 | unit: '枚',
10 | subtitle: '蓝色Q萌小公仔',
11 | value: 25,
12 | src: image0,
13 | started: false,
14 | empty: false,
15 | },
16 | {
17 | prizeId: '2',
18 | start: '12:00',
19 | title: '计算器笔记本',
20 | unit: '个',
21 | subtitle: '简单实用笔记本',
22 | value: 10,
23 | src: image1,
24 | started: false,
25 | empty: false,
26 | },
27 | {
28 | prizeId: '3',
29 | start: '14:00',
30 | title: '腾讯云公仔',
31 | unit: '枚',
32 | subtitle: '蓝色Q萌小公仔',
33 | value: 25,
34 | src: image0,
35 | started: false,
36 | empty: false,
37 | },
38 | {
39 | prizeId: 4,
40 | start: '17:00',
41 | title: '计算器笔记本',
42 | unit: '个',
43 | subtitle: '简单实用笔记本',
44 | value: 10,
45 | src: image1,
46 | started: false,
47 | empty: false,
48 | },
49 | ];
50 |
51 | const defaultPrizeCounts = {
52 | '1': 0,
53 | '2': 0,
54 | '3': 0,
55 | '4': 0,
56 | };
57 |
58 | const prizeLimitMap = {
59 | '1': 10,
60 | '2': 10,
61 | '3': 10,
62 | '4': 10,
63 | };
64 |
65 | const UID_KEY = 'gmtc_uid';
66 |
67 | export {
68 | defaultPrizes,
69 | defaultPrizeCounts,
70 | prizeLimitMap,
71 | UID_KEY,
72 | };
73 |
--------------------------------------------------------------------------------
/api/sls.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const express = require('express');
4 | const cors = require('cors');
5 | const bodyParser = require('body-parser');
6 | const UserController = require('./controller/user');
7 |
8 | const app = express();
9 | app.use(bodyParser.json());
10 | app.use(cors());
11 |
12 | app.get('/', (req, res) => {
13 | res.send(
14 | JSON.stringify({
15 | code: 0,
16 | message: `Server time: ${new Date().toString()}`,
17 | }),
18 | );
19 | });
20 |
21 | app.get('/flush', async (req, res) => {
22 | const data = await UserController.deleteEmptyName();
23 | res.send(
24 | JSON.stringify({
25 | code: 0,
26 | data,
27 | message: 'Flush database Success',
28 | }),
29 | );
30 | });
31 |
32 | // get user list
33 | app.get('/user', async (req, res) => {
34 | const data = await UserController.getUserList();
35 | res.send(
36 | JSON.stringify({
37 | code: 0,
38 | data,
39 | }),
40 | );
41 | });
42 |
43 | // add new user
44 | app.post('/user', async (req, res) => {
45 | let result = '';
46 | try {
47 | const user = req.body;
48 | const data = await UserController.createUser(user);
49 | result = {
50 | code: 0,
51 | data,
52 | message: 'Insert Success',
53 | };
54 | } catch (e) {
55 | result = {
56 | code: e.code,
57 | message: `Insert Fail: ${e.message}`,
58 | };
59 | }
60 |
61 | res.send(JSON.stringify(result));
62 | });
63 |
64 | // delete user
65 | app.delete('/user/:name', async (req, res) => {
66 | let result = '';
67 | try {
68 | const { name } = req.params;
69 | const data = await UserController.deleteUserByName(name);
70 | result = {
71 | code: 0,
72 | data,
73 | message: 'Delete Success',
74 | };
75 | } catch (e) {
76 | result = {
77 | code: 1002,
78 | data: e,
79 | message: 'Delete Fail',
80 | };
81 | }
82 |
83 | res.send(JSON.stringify(result));
84 | });
85 |
86 | module.exports = app;
87 |
--------------------------------------------------------------------------------
/frontend/src/components/UserForm.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
User Form
4 |
5 |
44 |
45 |
46 |
65 |
99 |
--------------------------------------------------------------------------------
/api/controller/user.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { Pool } = require('pg');
4 |
5 | function ApiError(code, msg) {
6 | const e = new Error(msg);
7 | e.code = code;
8 | return e;
9 | }
10 |
11 | let pgPool;
12 |
13 | module.exports = {
14 | async getPool() {
15 | if (!pgPool) {
16 | pgPool = new Pool({
17 | connectionString: process.env.PG_CONNECT_STRING,
18 | });
19 | // init table
20 | await pgPool.query(`CREATE TABLE IF NOT EXISTS users (
21 | ID serial NOT NULL,
22 | NAME TEXT NOT NULL,
23 | EMAIL CHAR(50) NOT NULL,
24 | SITE CHAR(50) NOT NULL
25 | );`);
26 | return pgPool;
27 | } else {
28 | return pgPool;
29 | }
30 | },
31 | async getUserList() {
32 | const pool = await this.getPool();
33 | const client = await pool.connect();
34 | const { rows } = await client.query({
35 | text: 'select * from users',
36 | });
37 | await client.end();
38 | return rows;
39 | },
40 | async createUser(user) {
41 | const pool = await this.getPool();
42 | const { name, email, site } = user;
43 | const existUser = await this.getUserByName(name);
44 | if (existUser) {
45 | throw new ApiError(1000, `Name ${name} exist.`);
46 | }
47 | const client = await pool.connect();
48 | const { rowCount } = await client.query({
49 | text: 'INSERT INTO users(name, email, site) VALUES($1, $2, $3)',
50 | values: [name, email, site],
51 | });
52 | await client.end();
53 | return rowCount === 1;
54 | },
55 | async getUserByName(name) {
56 | try {
57 | const pool = await this.getPool();
58 | const client = await pool.connect();
59 | const { rows } = await client.query({
60 | text: 'SELECT * FROM users WHERE name = $1',
61 | values: [name],
62 | });
63 | await client.end();
64 | if (rows.length > 0) {
65 | return rows;
66 | }
67 | return false;
68 | } catch (e) {
69 | throw new ApiError(1001, e);
70 | }
71 | },
72 | async deleteUserByName(name) {
73 | const pool = await this.getPool();
74 | const client = await pool.connect();
75 | const { rows } = await client.query({
76 | text: 'DELETE FROM users WHERE name = $1',
77 | values: [name],
78 | });
79 | await client.end();
80 | return rows;
81 | },
82 | };
83 |
--------------------------------------------------------------------------------
/frontend/src/style/app.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'serverless';
3 | src: url('../assets/fonts/serverless-regular.woff2') format('woff2'),
4 | url('../assets/fonts/serverless-regular.woff') format('woff');
5 | font-weight: normal;
6 | font-style: normal;
7 | }
8 |
9 | html,
10 | body {
11 | display: flex;
12 | flex-direction: column;
13 | box-sizing: border-box;
14 | height: 100%;
15 | width: 100%;
16 | margin: 0;
17 | padding: 0;
18 | line-height: normal;
19 | background: var(--mainBg);
20 | font-family: 'serverless', 'HelveticaNeue-Regular', 'Helvetica Neue Regular',
21 | 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
22 | font-weight: 300;
23 | font-size: 14px;
24 | line-height: 20px;
25 | color: #fff;
26 | word-wrap: break-word;
27 | letter-spacing: 0.25px;
28 | }
29 |
30 | html,
31 | body {
32 | background: #000000;
33 | }
34 |
35 | .container {
36 | flex-direction: column;
37 | height: 100%;
38 | width: 100%;
39 | margin: 0;
40 | padding: 30px 50px 50px 50px;
41 | width: 1220px;
42 | box-sizing: border-box;
43 | margin: 0 auto;
44 | }
45 |
46 | section.title {
47 | display: flex;
48 | overflow: hidden;
49 | justify-content: space-between;
50 | align-items: center;
51 | height: 460px;
52 | }
53 |
54 | .left {
55 | float: left;
56 | }
57 |
58 | .right {
59 | float: right;
60 | }
61 |
62 | .title-left {
63 | }
64 |
65 | .title-left h1 {
66 | font-family: Soleil;
67 | font-size: 66px;
68 | line-height: 80px;
69 | margin: 42px 0 32px 0px;
70 | letter-spacing: -2px;
71 | }
72 |
73 | .title-left h3 {
74 | display: flex;
75 | margin: 0 0 80px;
76 | font-family: Soleil;
77 | font-size: 24px;
78 | line-height: 36px;
79 | }
80 |
81 | .title-right {
82 | width: 550px;
83 | }
84 |
85 | .title-gif {
86 | width: 550px;
87 | }
88 |
89 | .line {
90 | width: 100%;
91 | height: 2px;
92 | background-color: #fff;
93 | margin: 20px auto;
94 | }
95 |
96 | .content-left {
97 | text-align: center;
98 | }
99 |
100 | .hero {
101 | display: flex;
102 | align-items: center;
103 | justify-content: center;
104 | height: auto;
105 | min-height: 40vh;
106 | animation-duration: 2000ms;
107 | animation-name: fadeIn;
108 | }
109 |
110 | .hero img {
111 | max-height: 40vh;
112 | pointer-events: none;
113 | }
114 |
115 | .tagline {
116 | align-items: center;
117 | height: auto;
118 | margin-top: 40px;
119 | color: #ffffff;
120 | font-size: 18px;
121 | text-align: center;
122 | opacity: 0;
123 | animation-fill-mode: forwards;
124 | animation-delay: 500ms;
125 | animation-duration: 2000ms;
126 | animation-name: fadeIn;
127 | }
128 |
129 | .user-form {
130 | color: #fff;
131 | width: 500px;
132 | margin: 10px auto;
133 | padding: 20px;
134 | border: 2px solid #fff;
135 | display: flex;
136 | flex-direction: column;
137 | box-sizing: border-box;
138 | }
139 |
140 | .user-form .submit-btn {
141 | height: 30px;
142 | margin-top: 20px;
143 | cursor: pointer;
144 | }
145 |
146 | .form-item {
147 | margin: 10px 0;
148 | height: 40px;
149 | color: #fff;
150 | display: flex;
151 | flex-direction: column;
152 | box-sizing: border-box;
153 | }
154 |
155 | .form-item input {
156 | height: 24px;
157 | font-size: 24px;
158 | line-height: 24px;
159 | }
160 |
161 | .user-list {
162 | color: #fff;
163 | width: 500px;
164 | margin: 10px auto;
165 | padding: 20px;
166 | border: 2px solid #fff;
167 | }
168 |
169 | .user-wrapper {
170 | margin-top: 20px;
171 |
172 | }
173 |
174 | /**
175 | * Animations
176 | */
177 |
178 | @keyframes fadeIn {
179 | from {
180 | opacity: 0;
181 | }
182 |
183 | to {
184 | opacity: 1;
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | !!!此项目已迁移至 https://github.com/serverless-components/tencent-examples/tree/master/fullstack
2 |
3 | ## 操作场景
4 |
5 | 该模板可以快速部署一个基于 Vue + Express + PostgreSQL 的全栈 Serverless 应用。主
6 | 要包含以下组件:
7 |
8 | - Serverless RESTful API:通过**云函数**和 **API 网关**构建的 Express 框架实现
9 | RESTful API。
10 | - Serverless 静态网站:前端通过托管 Vue.js 静态页面到 **COS 对象存储**中。
11 | - PostgreSQL Serverless:通过创建 **PostgreSQL DB** 为全栈网站提供数据库服务。
12 | - VPC:通过创建 **VPC** 和 **子网**,提供 SCF 云函数和数据库的网络打通和使用。
13 |
14 | ## 前提条件
15 |
16 | - 已安装 [Node.js](https://nodejs.org/en/)(Node.js 版本需不低于 8.6,建议使用
17 | Node.js10.0 及以上版本)
18 |
19 | ## 操作步骤
20 |
21 | ### 安装
22 |
23 | 通过 npm 全局安装
24 | [Serverless Framework](https://github.com/serverless/serverless):
25 |
26 | ```shell
27 | $ npm install -g serverless
28 | ```
29 |
30 | 如果之前您已经安装过 Serverless Framework,可以通过下列命令升级到最新版:
31 |
32 | ```shell
33 | $ npm update -g serverless
34 | ```
35 |
36 | 安装完毕后,通过运行 serverless -v 命令,查看 Serverless Framework 的版本信息,
37 | 确保版本信息不低于以下版本:
38 |
39 | ```shell
40 | $ serverless –v
41 | Framework Core: 1.67.3
42 | Plugin: 3.6.6
43 | SDK: 2.3.0
44 | Components: 2.30.1
45 | ```
46 |
47 | ### 配置
48 |
49 | 1.新建一个本地文件夹,使用`create --template-url`命令,下载相关 template。
50 |
51 | ```bash
52 | serverless create --template-url https://github.com/serverless-components/tencent-fullstack
53 | ```
54 |
55 | 2.在项目模板中找到.env.example 文件,修改名称为.env,并在其中配置对应的腾讯云
56 | SecretId 和 SecretKey 信息、地域可用区及子网等信息。
57 |
58 | ```text
59 | # .env
60 | TENCENT_SECRET_ID=xxx // 您账号的SecretId
61 | TENCENT_SECRET_KEY=xxx // 您账号的SecretKey
62 |
63 | # 地域可用区配置
64 | REGION=ap-beijing //资源部署区,该项目中指云函数与静态页面部署区
65 | ZONE=ap-beijing-3 //资源部署可用区 ,该项目中指DB部署所在的可用区
66 | ```
67 |
68 | > 说明:
69 |
70 | - 如果没有腾讯云账号,请先 [注册新账号](https://cloud.tencent.com/register)。
71 | - 如果已有腾讯云账号,请保证您的账号已经授权了 AdministratorAccess 权限。 您可以
72 | 在 [API 密钥管理](https://console.cloud.tencent.com/cam/capi) 中获取 SecretId
73 | 和 SecretKey。
74 | - ZONE 目前只支持 ap-beijing-3 、ap-guangzhou-2、ap-shanghai-2。
75 | - 您可以在子网中获取 VPC_ID 和 SUBNET_ID,**请务必保证和 ZONE 在同一个可用区**。
76 |
77 | 3.通过执行以下命令,安装所需依赖:
78 |
79 | ```bash
80 | $ npm run bootstrap
81 | ```
82 |
83 | ### 部署
84 |
85 | 1.执行以下命令进行部署.
86 |
87 | ```bash
88 | $ sls deploy --all
89 |
90 | serverless ⚡ framework
91 |
92 | serverlessVpc:
93 | region: ap-guangzhou
94 | zone: ap-guangzhou-2
95 | vpcId: vpc-xxx
96 | vpcName: serverless
97 | subnetId: subnet-xxx
98 | subnetName: serverless
99 |
100 | fullstackDB:
101 | region: ap-guangzhou
102 | zone: ap-guangzhou-2
103 | vpcConfig:
104 | subnetId: subnet-100000
105 | vpcId: vpc-1000000
106 | dBInstanceName: fullstackDB
107 | dBInstanceId: postgres-100000
108 | private:
109 | connectionString: postgresql://tencentdb_100000xxxxxxxxxxxxx@172.16.250.15:5432/tencentdb_1000000
110 | host: 172.16.250.15
111 | port: 5432
112 | user: tencentdb_100000
113 | password: xxxxxxxx
114 | dbname: tencentdb_100000
115 |
116 | fullstack-api:
117 | region: ap-guangzhou
118 | apigw:
119 | serviceId: service-100000
120 | subDomain: service-100000-123456789.gz.apigw.tencentcs.com
121 | environment: release
122 | url: https://service-100000-123456789.gz.apigw.tencentcs.com/release/
123 | scf:
124 | functionName: fullstack-api
125 | runtime: Nodejs10.15
126 | namespace: default
127 |
128 | fullstack-frontend:
129 | website: https://fullstack-serverless-db-123456789.cos-website.ap-guangzhou.myqcloud.com
130 |
131 | 50s › tencent-fullstack › Success
132 | ```
133 |
134 | 部署成功后,您可以使用浏览器访问项目产生的 website 链接,就可以看到生成的网站了
135 | 。
136 |
137 | 2.执行 npm run info 查看部署信息,该项目部署的信息分三部分:db、api、frontend(
138 | 前端网站)。
139 |
140 | ```bash
141 | $ npm run info
142 | > tencent-fullstack@1.1.0 info /root/tencent-fullstack
143 | > npm run info:vpc && npm run info:db && npm run info:api && npm run info:frontend
144 |
145 | > tencent-fullstack@1.1.0 info:vpc /Users/yugasun/Desktop/Develop/@yugasun/tencent-fullstack
146 | > sls info --target=./vpc
147 |
148 |
149 | serverless ⚡ framework
150 |
151 | Status: active
152 | Last Action: deploy (5 minutes ago)
153 | Deployments: 1
154 |
155 | region: ap-guangzhou
156 | zone: ap-guangzhou-2
157 | vpcId: vpc-xxx
158 | vpcName: serverless
159 | subnetId: subnet-xxx
160 | subnetName: serverless
161 |
162 | serverlessVpc › Info successfully loaded
163 |
164 |
165 | > tencent-fullstack@1.1.0 info:db /root/tencent-fullstack
166 | > sls info --target=./db
167 |
168 |
169 | serverless ⚡ framework
170 |
171 | Status: active
172 | Last Action: deploy (3 minutes ago)
173 | Deployments: 18
174 |
175 | region: ap-guangzhou
176 | zone: ap-guangzhou-2
177 | vpcConfig:
178 | subnetId: subnet-100000
179 | vpcId: vpc-1000000
180 | dBInstanceName: fullstackDB
181 | dBInstanceId: postgres-100000
182 | private:
183 | connectionString: postgresql://tencentdb_100000xxxxxxxxxxxxxxxxxxx@172.16.250.15:5432/tencentdb_100000
184 | host: 172.16.250.15
185 | port: 5432
186 | user: tencentdb_1000000
187 | password: xxxxxxxxx
188 | dbname: tencentdb_1000000
189 |
190 | fullstackDB › Info successfully loaded
191 |
192 |
193 | > tencent-fullstack@1.1.0 info:api /root/tencent-fullstack
194 | > sls info --target=./api
195 |
196 |
197 | serverless ⚡ framework
198 |
199 | Status: active
200 | Last Action: deploy (2 minutes ago)
201 | Deployments: 10
202 |
203 | region: ap-guangzhou
204 | apigw:
205 | serviceId: service-1000000
206 | subDomain: service-1000000-123456789.gz.apigw.tencentcs.com
207 | environment: release
208 | url: https://service-1000000-123456789.gz.apigw.tencentcs.com/release/
209 | scf:
210 | functionName: fullstack-api
211 | runtime: Nodejs10.15
212 | namespace: default
213 |
214 | fullstack-api › Info successfully loaded
215 |
216 |
217 | > tencent-fullstack@1.1.0 info:frontend /root/tencent-fullstack
218 | > sls info --target=./frontend
219 |
220 |
221 | serverless ⚡ framework
222 |
223 | Status: active
224 | Last Action: deploy (2 minutes ago)
225 | Deployments: 9
226 |
227 | website: https://fullstack-serverless-db-123456789.cos-website.ap-guangzhou.myqcloud.com
228 |
229 | fullstack-frontend › Info successfully loaded
230 | ```
231 |
232 | 3.执行 sls remove --all,可移除项目。
233 |
234 | ```bash
235 | $ sls remove --all
236 |
237 | serverless ⚡ framework
238 |
239 | 38s › tencent-fullstack › Success
240 | ```
241 |
--------------------------------------------------------------------------------
/README.en.md:
--------------------------------------------------------------------------------
1 | !!!This project had been moved to https://github.com/serverless-components/tencent-examples/tree/master/fullstack
2 |
3 | # fullstack-serverless-db
4 |
5 | This is a template of serverless fullstack application. It aims to be the
6 | simplest possible way to build a serverless fullstack application, including a
7 | Vue.js application on the front-end bundled with Parcel and back-end API using
8 | postgresql.
9 |
10 | This template includes:
11 |
12 | - **Serverless RESTful API**: Using
13 | [@serverless/tencent-express](https://github.com/serverless-components/tencent-express/tree/v2)
14 | component, it contains a Servelress Cloud Function and a single API Gateway
15 | endpoint.
16 |
17 | - **Serverless website using Vue.js**:
18 | [@serverless/tencent-website](https://github.com/serverless-components/tencent-website/tree/v2),
19 | it deploys all static files to Cloud Object Storage.
20 |
21 | - **Serverless Postgresql**:
22 | [@serverless/tencent-postgresql](https://github.com/serverless-components/tencent-postgresql/tree/v2),
23 | it auto create a postgresql database for backend using.
24 |
25 |
26 |
27 | 1. [Prepare](#Prepare)
28 | 2. [Download](#Download)
29 | 3. [Bootstrap](#Bootstrap)
30 | 4. [Deploy](#Deploy)
31 | 5. [Development](#Development)
32 |
33 |
34 |
35 | ### Prepare
36 |
37 | Before all below steps, you should install
38 | [Serverless Framework](https://www.github.com/serverless/serverless) globally:
39 |
40 | ```bash
41 | $ npm i serverless -g
42 | ```
43 |
44 | ### Download
45 |
46 | Severless cli is very convenient, it can download templates in any github
47 | project which should contain `serverless.yml` file.
48 |
49 | ```bash
50 | $ serverless create --template-url https://github.com/serverless-components/tencent-fullstack
51 | ```
52 |
53 | ### Bootstrap
54 |
55 | Copy `.env.example` file to `.env` in project root:
56 |
57 | Add the access keys of a
58 | [Tencent CAM Role](https://console.cloud.tencent.com/cam/capi) with
59 | `AdministratorAccess` in the `.env` file, like below:
60 |
61 | ```dotenv
62 | # .env
63 | TENCENT_SECRET_ID=xxx
64 | TENCENT_SECRET_KEY=xxx
65 |
66 | # change to your requirement
67 | REGION=ap-guangzhou
68 | ZONE=ap-guangzhou-2
69 | VPC_ID=vpc-xxx
70 | SUBNET_ID=subnet-xxx
71 | ```
72 |
73 | Install the NPM dependencies:
74 |
75 | ```bash
76 | $ npm run bootstrap
77 | ```
78 |
79 | ### Support commands
80 |
81 | Deploy:
82 |
83 | ```bash
84 | $ sls deploy --all
85 |
86 | serverless ⚡ framework
87 |
88 | serverlessVpc:
89 | region: ap-guangzhou
90 | zone: ap-guangzhou-2
91 | vpcId: vpc-xxx
92 | vpcName: serverless
93 | subnetId: subnet-xxx
94 | subnetName: serverless
95 |
96 | fullstackDB:
97 | region: ap-guangzhou
98 | zone: ap-guangzhou-2
99 | vpcConfig:
100 | subnetId: subnet-100000
101 | vpcId: vpc-1000000
102 | dBInstanceName: fullstackDB
103 | dBInstanceId: postgres-100000
104 | private:
105 | connectionString: postgresql://tencentdb_100000xxxxxxxxxxxxx@172.16.250.15:5432/tencentdb_1000000
106 | host: 172.16.250.15
107 | port: 5432
108 | user: tencentdb_100000
109 | password: xxxxxxxx
110 | dbname: tencentdb_100000
111 |
112 | fullstack-api:
113 | region: ap-guangzhou
114 | apigw:
115 | serviceId: service-100000
116 | subDomain: service-100000-123456789.gz.apigw.tencentcs.com
117 | environment: release
118 | url: https://service-100000-123456789.gz.apigw.tencentcs.com/release/
119 | scf:
120 | functionName: fullstack-api
121 | runtime: Nodejs10.15
122 | namespace: default
123 |
124 | fullstack-frontend:
125 | website: https://fullstack-serverless-db-123456789.cos-website.ap-guangzhou.myqcloud.com
126 |
127 | 50s › tencent-fullstack › Success
128 | ```
129 |
130 | Get deploy info:
131 |
132 | ```bash
133 | $ npm run info
134 | > tencent-fullstack@1.1.0 info /root/tencent-fullstack
135 | > npm run info:vpc && npm run info:db && npm run info:api && npm run info:frontend
136 |
137 | > tencent-fullstack@1.1.0 info:vpc /Users/yugasun/Desktop/Develop/@yugasun/tencent-fullstack
138 | > sls info --target=./vpc
139 |
140 |
141 | serverless ⚡ framework
142 |
143 | Status: active
144 | Last Action: deploy (5 minutes ago)
145 | Deployments: 1
146 |
147 | region: ap-guangzhou
148 | zone: ap-guangzhou-2
149 | vpcId: vpc-xxx
150 | vpcName: serverless
151 | subnetId: subnet-xxx
152 | subnetName: serverless
153 |
154 | serverlessVpc › Info successfully loaded
155 |
156 | > tencent-fullstack@1.1.0 info:db /root/tencent-fullstack
157 | > sls info --target=./db
158 |
159 |
160 | serverless ⚡ framework
161 |
162 | Status: active
163 | Last Action: deploy (3 minutes ago)
164 | Deployments: 18
165 |
166 | region: ap-guangzhou
167 | zone: ap-guangzhou-2
168 | vpcConfig:
169 | subnetId: subnet-100000
170 | vpcId: vpc-1000000
171 | dBInstanceName: fullstackDB
172 | dBInstanceId: postgres-100000
173 | private:
174 | connectionString: postgresql://tencentdb_100000xxxxxxxxxxxxxxxxxxx@172.16.250.15:5432/tencentdb_100000
175 | host: 172.16.250.15
176 | port: 5432
177 | user: tencentdb_1000000
178 | password: xxxxxxxxx
179 | dbname: tencentdb_1000000
180 |
181 | fullstackDB › Info successfully loaded
182 |
183 |
184 | > tencent-fullstack@1.1.0 info:api /root/tencent-fullstack
185 | > sls info --target=./api
186 |
187 |
188 | serverless ⚡ framework
189 |
190 | Status: active
191 | Last Action: deploy (2 minutes ago)
192 | Deployments: 10
193 |
194 | region: ap-guangzhou
195 | apigw:
196 | serviceId: service-1000000
197 | subDomain: service-1000000-123456789.gz.apigw.tencentcs.com
198 | environment: release
199 | url: https://service-100000-123456789.gz.apigw.tencentcs.com/release/
200 | scf:
201 | functionName: fullstack-api
202 | runtime: Nodejs10.15
203 | namespace: default
204 |
205 | fullstack-api › Info successfully loaded
206 |
207 |
208 | > tencent-fullstack@1.1.0 info:frontend /root/tencent-fullstack
209 | > sls info --target=./frontend
210 |
211 |
212 | serverless ⚡ framework
213 |
214 | Status: active
215 | Last Action: deploy (2 minutes ago)
216 | Deployments: 9
217 |
218 | website: https://fullstack-serverless-db-123456789.cos-website.ap-guangzhou.myqcloud.com
219 |
220 | fullstack-frontend › Info successfully loaded
221 | ```
222 |
223 | Remove:
224 |
225 | ```bash
226 | $ sls remove --all
227 |
228 | serverless ⚡ framework
229 |
230 | 38s › tencent-fullstack › Success
231 | ```
232 |
233 | ### License
234 |
235 | MIT
236 |
237 | ```
238 |
239 | ```
240 |
--------------------------------------------------------------------------------
/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
13 |
14 |
19 |
20 |
21 |
22 |
Next generation, serverless cloud
23 |
24 | Tencent Cloud and Serverless have joined forces to create a
25 | serverless cloud focused on delivering outcomes, not
26 | infrastructure. Everything autoscaling; never pay for idle.
27 |
28 |
29 |
30 |
34 |
35 | This is a fullstack app built on tencent serverless components via the serverless
36 | framework
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
133 |
134 |
140 |
--------------------------------------------------------------------------------