Skip to content

Instantly share code, notes, and snippets.

@SheepTester
Created August 21, 2019 01:13
Show Gist options
  • Select an option

  • Save SheepTester/1df51d1fe8ef15a94d4f747b28dc115a to your computer and use it in GitHub Desktop.

Select an option

Save SheepTester/1df51d1fe8ef15a94d4f747b28dc115a to your computer and use it in GitHub Desktop.
F Word backend using Firebase

F Word backend code

I censored some code because I put all my Firebase code in a single file.

You should probably change adminuid to something else should you use this.

Try F Word here.

{
"rules": {
"leaderboard": {
".read": "auth !== null && auth.uid === 'adminuid'",
".write": "auth !== null && auth.uid === 'adminuid'"
},
"fgr-leaderboard": {
".read": "auth !== null && auth.uid === 'adminuid'",
".write": "auth !== null && auth.uid === 'adminuid'"
},
"f-words": {
".read": "auth !== null && auth.uid === 'adminuid'",
".write": "auth !== null && auth.uid === 'adminuid'"
}
}
}
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [ {
"source": "/fword", "function": "fword"
} ]
}
}
const functions = require('firebase-functions');
const admin = require("firebase-admin");
const cors = require('cors')({origin: true});
const md5 = require('md5');
admin.initializeApp({
databaseURL: "https://test-9d9aa.firebaseio.com",
databaseAuthVariableOverride: {
uid: "adminuid"
}
});
const db = admin.database();
const fWordDB = db.ref('/f-words');
// lowercase letters, spaces ok, but no double spaces, cannot start/end with spaces
const usernameValidator = /^([a-z]([a-z]| (?! ))+[a-z]|[a-z]+)$/;
const pfpValidator = /^\d{1,3}(\.[a-z0-9]{6}){2}$/i;
function getVal(ref) {
return new Promise((res, rej) => {
ref.once("value", res, rej);
});
}
exports.fword = functions.https.onRequest((req, res) => {
return cors(req, res, () => {
function announceError(err) {
res.status(400).send(err instanceof Error ? err.message : err);
}
const now = Date.now();
res.set('Access-Control-Allow-Origin', '*');
if (req.query.session) {
const sessionRef = fWordDB.child('sessions/' + req.body.session);
getVal(sessionRef).then(snap => {
return snap.exists() ? snap : Promise.reject('session doesn\'t exist');
}).then(async snap => {
const {user, expiration_date} = snap.val();
if (expiration_date !== -1 && expiration_date < now) {
sessionRef.remove();
return Promise.reject('expired session');
} else {
if (req.query.type === 'signout') {
// ?session=true&type=signout
sessionRef.remove();
res.status(200).end();
} else if (req.query.type === 'setexpiry') {
// ?session=true&type=setexpiry
// {time}
const time = +req.body.time || -1;
if (time !== -1 && time < 300000) return Promise.reject('too short');
fWordDB.child('users/' + user + '/expiry_length').set(time);
res.status(200).end();
} else if (req.query.type === 'setbio') {
// ?session=true&type=setbio
// {content}
if (req.body.content.length > 500) return Promise.reject('too long');
fWordDB.child('users/' + user + '/bio').set(req.body.content);
res.status(200).end();
} else if (req.query.type === 'setpfp') {
// ?session=true&type=setpfp
// {newpfp}
if (!pfpValidator.test(req.body.newpfp)) return Promise.reject('not a pfp');
fWordDB.child('users/' + user + '/pfp').set(req.body.newpfp);
res.status(200).end();
} else if (req.query.type === 'setpass') {
// ?session=true&type=setpass
// {password}
if (!req.body.password.includes(' ')) return Promise.reject('dumb password');
fWordDB.child('passwords/' + user).set(md5(req.body.password + user));
res.status(200).end();
} else if (req.query.type === 'setsecret') {
// ?session=true&type=setsecret
// {content}
if (req.body.content.length > 500) return Promise.reject('too long');
fWordDB.child('users/' + user + '/secret').set(req.body.content);
res.status(200).end();
} else if (req.query.type === 'secret') {
// ?session=true&type=secret
res.status(200).send((await getVal(fWordDB.child('users/' + user + '/secret'))).val());
} else if (req.query.type === 'post') {
// ?session=true&type=post
// {content, parent?}
// returns post ID
const content = req.body.content.trim();
if (content.length > 500) return Promise.reject('too long');
else if (content.length <= 0) return Promise.reject('too short');
const postRef = fWordDB.child('posts').push();
postRef.set({
author: user,
content: content,
date: now,
parent: req.body.parent || null
});
fWordDB.child('users/' + user + '/posts/' + postRef.key).set({content, author: user});
res.status(200).send(postRef.key);
if (req.body.parent) {
fWordDB.child('posts/' + req.body.parent + '/children/' + postRef.key).set({content, author: user});
}
} else if (req.query.type === 'like') {
// ?session=true&type=like&post
// returns like count
const author = (await getVal(fWordDB.child('posts/' + req.query.post + '/author'))).val();
const content = (await getVal(fWordDB.child('posts/' + req.query.post + '/content'))).val();
if (author === null) return Promise.reject('post does not exist');
fWordDB.child('posts/' + req.query.post + '/likes/' + user).set(true);
fWordDB.child('users/' + user + '/likes/' + req.query.post).set({content, author});
res.status(200).send((await getVal(fWordDB.child('users/' + user + '/likes'))).numChildren().toString());
} else if (req.query.type === 'unlike') {
// ?session=true&type=unlike&post
// returns like count
fWordDB.child('posts/' + req.query.post + '/likes/' + user).remove();
fWordDB.child('users/' + user + '/likes/' + req.query.post).remove();
res.status(200).send((await getVal(fWordDB.child('users/' + user + '/likes'))).numChildren().toString());
} else if (req.query.type === 'user-likes') {
// ?session=true&type=user-likes&post
// returns boolean
res.status(200).send((await getVal(fWordDB.child('users/' + user + '/likes/' + req.query.post))).exists().toString());
} else if (req.query.type === 'delete') {
// ?session=true&type=delete&post
const admin = (await getVal(fWordDB.child('users/' + user + '/admin'))).val() === 'yes';
if (!admin) return Promise.reject('not admin');
const postRef = fWordDB.child('posts/' + req.query.post);
const postData = (await getVal(postRef)).val();
if (postData.likes) {
Object.keys(postData.likes).forEach(liker => {
fWordDB.child('users/' + liker + '/likes/' + req.query.post).remove();
});
}
fWordDB.child('trash/' + req.query.post).set(postData);
postRef.remove();
fWordDB.child('users/' + postData.author + '/posts/' + req.query.post).remove();
res.status(200).json(postData);
} else {
// ?session=true
// returns username
res.status(200).send(user);
}
}
}).catch(announceError);
} else {
if (req.query.type === 'signin') {
// ?type=signin
// {user, password}
// returns session ID
const user = req.body.user;
const password = md5(req.body.password + user);
getVal(fWordDB.child('passwords/' + user)).then(snap => {
const actualPassword = snap.val();
if (actualPassword === password) {
return getVal(fWordDB.child('users/' + user + '/expiry_length'));
} else {
return Promise.reject('incorrect password');
}
}).then(snap => {
const expiryLength = snap.val();
const session = md5(now + user + Math.random());
fWordDB.child('sessions/' + session).set({
expiration_date: expiryLength === -1 ? -1 : expiryLength + now,
user: user
});
res.status(200).send(session);
}).catch(announceError);
} else if (req.query.type === 'createuser') {
// ?type=createuser
// {user, password}
// returns session ID
const user = req.body.user;
if (user.length < 1 || user.length > 26 || !usernameValidator.test(user)) {
res.status(400).send('dumb username');
return;
}
if (!req.body.password.includes(' ')) {
res.status(400).send('dumb password');
return;
}
const password = md5(req.body.password + user);
const passwordRef = fWordDB.child('passwords/' + user);
getVal(passwordRef).then(snap => {
if (snap.exists()) return Promise.reject('user exists');
else {
passwordRef.set(password);
const colour = Math.floor(Math.random() * 0x1000000).toString(16).padStart(6, '0');
fWordDB.child('users/' + user).set({
bio: 'Hi! I\'m so new that I haven\'t even set my bio yet!',
expiry_length: -1,
joinDate: now,
pfp: Math.floor(Math.random() * 360) + '.' + colour + '.' + colour,
secret: ''
});
const session = md5(now + user + Math.random());
fWordDB.child('sessions/' + session).set({
expiration_date: -1,
user: user
});
res.status(200).send(session);
}
}).catch(announceError);
} else if (req.query.type === 'user') {
// ?type=user&user
// returns user bio, profile picture, join date
getVal(fWordDB.child('users/' + req.query.user)).then(snap => {
if (!snap.exists()) return Promise.reject('user doesn\'t exist');
const {bio, pfp, joinDate} = snap.val();
res.status(200).json({bio, pfp, joinDate});
}).catch(announceError);
} else if (req.query.type === 'post') {
// ?type=post&post
// returns post data
getVal(fWordDB.child('posts/' + req.query.post)).then(snap => {
if (!snap.exists()) return Promise.reject('post doesn\'t exist');
res.status(200).json(snap.val());
}).catch(announceError);
} else if (req.query.type === 'pfps') {
// ?type=pfps&users
// returns post data
const pfps = {};
Promise.all(req.query.users.split('.').map(async user => {
const pfp = await getVal(fWordDB.child('users/' + user + '/pfp'));
if (pfp) pfps[user] = pfp;
})).then(() => {
res.status(200).json(pfps);
});
} else if (req.query.type === 'recent-posts') {
// ?type=recent-posts&limit&from
// returns posts
const limit = Math.round(+req.query.limit);
if (isNaN(limit)) res.status(400).send("limit not a number");
else if (limit < 1) return res.status(400).send("limit too small");
else if (limit > 100) return res.status(400).send("limit too big");
let ref = fWordDB.child('posts').orderByKey();
if (req.query.from) ref = ref.endAt(req.query.from);
getVal(ref.limitToLast(limit)).then(snap => {
res.status(200).json(snap.val());
}).catch(announceError);
} else if (req.query.type === 'user-posts') {
// ?type=user-posts&user
// returns post previews
getVal(fWordDB.child('users/' + req.query.user + '/posts')).then(snap => {
if (snap.exists()) res.status(200).json(snap.val());
else res.status(200).json({});
}).catch(announceError);
} else if (req.query.type === 'user-likes') {
// ?type=user-likes&user
// returns post previews
getVal(fWordDB.child('users/' + req.query.user + '/likes')).then(snap => {
if (snap.exists()) res.status(200).json(snap.val());
else res.status(200).json({});
}).catch(announceError);
} else {
// returns "Hello!"
res.status(200).send("Hello!");
}
}
});
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment