server part 功能包括
typeorm 的 orm entity 規劃資料表與orm使用
apolloServer type graphql 的設計與 實作 query 與 mutation
(如果沒有使用過graphql的朋友可以想做打api格式跟實作回應的就可以了)
passport 第三方認證登入 並發出 jwt token,娃娃機 目前有用到的是fb 與 google 模式
片段程式說明 以fb為例 先去申請一組 app https://developers.facebook.com/
需要 clientID與SECRET
import FacebookStrategy from 'passport-facebook';
import express from 'express'
...略 express setup
passport.use(
new FacebookStrategy.Strategy(
{ clientID: clientID, clientSecret: SECRET, callbackURL: 'https://clawfun.online/callbackurl', profileFields: ['id', 'email', 'displayName', 'photos'] },
async (accessToken: any, refreshToken: any, profile: any, cb: any) => {
const { id, displayName, _json } = profile;
return cb(null, { member: profile, accessToken, refreshToken });
},
),
);
//express
app.get('/callbackurl',
passport.authenticate('facebook', { session: false }),
async (req: any, res) => {
const member = _.get(req, 'user.profile')
const accessToken = _.get(req, 'user.accessToken');
const refreshToken = _.get(req, 'user.refreshToken');
try {
const createJwtToken = await createTokens(req, member, SECRET);
res.redirect(`https://clawfun.online`);
} catch (err) {
res.redirect(`https://clawfun.online/fbauthfail`);
}
},
);
//簽Jwt 範例
export const createTokens = async (req: any, member: any, secret: any) => {
const tokenInfo = jwt.sign(member, secret, { expiresIn: '365d', });
return tokenInfo
};
ffmpeg 轉檔 當遊玩娃娃時候會開始錄影 當結束後會由 ts檔案 轉mp4檔案 讓使用者方便在前端瀏覽
稍後章節會在解說這個部分的程式碼
https://github.com/fluent-ffmpeg/node-fluent-ffmpeg
第三方金流 tappay 選用原因之一是他是可以不用轉跳頁面 使用者體驗更佳,在之後章節會再提一些開發心得
接受前端的api 後 ,根據條件發指令給娃娃機的溝通 ,所有結果 有分通知個人 與通知全部或是特定條件
所以這邊會用到 websocket 與 apollo subscribe
後端 apollo server 主要設定片段
const server = new ApolloServer({
schema: genSchema(), uploads: { maxFileSize: 10000000, maxFiles: 20 },
context: async ({ req }: { req: any }) => {
const tokenInfo = await jwtwork(req); return { ...tokenInfo, req, connection, allMachines, machineMap, pubsub }
},
tracing: false,
debug: false,
introspection: false,
subscriptions: {
onConnect: (connectionParams, webSocket, context) => {
...
},
onDisconnect: (webSocket, context) => {
...
},
},
})
server.applyMiddleware({ app, cors: { credentials: true, origin }, })
const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);
httpServer.listen({ port: 5002 }, () => {
console.log(`? GraphQl Server at 開發環境 is http://localhost:5002${server.graphqlPath}`);
console.log(`? GraphQl Server Subscriptions is ws://localhost:5002${server.subscriptionsPath}`)
})
除了與前端對接之外也要與娃娃機的 relay 機器對接 (娃娃機會自己把一些指令集傳到relay這台機等待被呼叫,relay機器要自己架設)
對街的資料要透過socket
大概做法例如傳一個指令集(
conn = net.createConnection(configs.SOCKET_PORT, configs.SOCKET_HOST);
...省略
machine.socket=conn
const header = Buffer.from([1]);
const id = Buffer.from(machine.wawajiId + ';');
const command = Buffer.from(wawajiControl.getCommand(cameraId, cmdId));
machine.socket.write(Buffer.concat([header, id, command]));
這邊的header 要根據娃娃機板子提供的規格去制定 後面的章節再詳細說明 這邊先提個大概
以上就大概是後端功能