add link sharing
This commit is contained in:
parent
2b95fc17a6
commit
dc2cec78dc
@ -37,7 +37,8 @@ class MiscUnit extends Unit {
|
|||||||
global.devbug = {
|
global.devbug = {
|
||||||
version: '0.2.0',
|
version: '0.2.0',
|
||||||
code: {
|
code: {
|
||||||
node: `dbsetup({
|
node: `require("devbugjs")
|
||||||
|
dbsetup({
|
||||||
\tserver: "https://CHANGEME:8000/", // DevBug Server URL
|
\tserver: "https://CHANGEME:8000/", // DevBug Server URL
|
||||||
\tproject: "CHANGEME", // Project API Key
|
\tproject: "CHANGEME", // Project API Key
|
||||||
})`,
|
})`,
|
||||||
|
@ -87,6 +87,24 @@ a {
|
|||||||
color: #eee;
|
color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
margin: 5;
|
||||||
|
margin-bottom: 20;
|
||||||
|
padding: 5;
|
||||||
|
padding-left: 10;
|
||||||
|
padding-right: 10;
|
||||||
|
background: #509d9d;
|
||||||
|
border-radius: 7px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
background: #eee;
|
||||||
|
color: #307d7d;
|
||||||
|
}
|
||||||
|
|
||||||
pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }
|
pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }
|
||||||
.string { color: darkslateblue; }
|
.string { color: darkslateblue; }
|
||||||
.number { color: darkorange; }
|
.number { color: darkorange; }
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
const Out = _flitter.model('v1:Out')
|
const Out = _flitter.model('v1:Out')
|
||||||
const Project = _flitter.model('v1:Project')
|
const Project = _flitter.model('v1:Project')
|
||||||
|
const Invite = _flitter.model('v1:Invite')
|
||||||
class v1 {
|
class v1 {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -66,6 +67,53 @@ class v1 {
|
|||||||
success: true
|
success: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async invite_show(req, res, next){
|
||||||
|
const invite = await Invite.findById(req.params.id)
|
||||||
|
if ( !invite ) _flitter.error(res, 404, {reason: 'Invitation not found with the specified URL.'})
|
||||||
|
|
||||||
|
if ( invite.used ) _flitter.error(res, 401, {reason: 'This invitation link has been used or has expired.'})
|
||||||
|
|
||||||
|
const project = await Project.findById(invite.project_id)
|
||||||
|
if ( !project ) _flitter.error(res, 404, {reason: 'This project no longer exists.'})
|
||||||
|
|
||||||
|
const user = await _flitter.model('User').findOne({uuid: invite.by_user_id})
|
||||||
|
if ( !user ) _flitter.error(res, 500, {reason: 'This user no longer exists. Sorry.'})
|
||||||
|
|
||||||
|
return _flitter.view(res, 'dash_v1:accept', {invite, project, user})
|
||||||
|
}
|
||||||
|
|
||||||
|
async invite_accept(req, res, next){
|
||||||
|
const invite = await Invite.findById(req.params.id)
|
||||||
|
if ( !invite ) _flitter.error(res, 404, {reason: 'Invitation not found with the specified URL.'})
|
||||||
|
|
||||||
|
if ( invite.used ) _flitter.error(res, 401, {reason: 'This invitation link has been used or has expired.'})
|
||||||
|
|
||||||
|
const project = await Project.findById(invite.project_id)
|
||||||
|
if ( !project ) _flitter.error(res, 404, {reason: 'This project no longer exists.'})
|
||||||
|
|
||||||
|
const user = await _flitter.model('User').findOne({uuid: invite.by_user_id})
|
||||||
|
if ( !user ) _flitter.error(res, 500, {reason: 'This user no longer exists. Sorry.'})
|
||||||
|
|
||||||
|
// if we're signed in
|
||||||
|
if ( req.session.auth && req.session.auth.user ){
|
||||||
|
if ( project.user_id !== req.session.auth.uuid && !project.shared_user_ids.includes(req.session.auth.uuid) ){
|
||||||
|
project.shared_user_ids.push(req.session.auth.uuid)
|
||||||
|
await project.save()
|
||||||
|
}
|
||||||
|
return res.redirect('/dash/v1')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
req.session.invite = true
|
||||||
|
req.session.invite_data = {
|
||||||
|
invite: invite.id,
|
||||||
|
project: project.id,
|
||||||
|
user: user.id,
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.redirect('/dash/v1')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = exports = v1
|
module.exports = exports = v1
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
const Project = _flitter.model('v1:Project')
|
const Project = _flitter.model('v1:Project')
|
||||||
const Out = _flitter.model('v1:Out')
|
const Out = _flitter.model('v1:Out')
|
||||||
|
const Invite = _flitter.model('v1:Invite')
|
||||||
class v1 {
|
class v1 {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -130,7 +131,7 @@ class v1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO permission access check
|
// TODO permission access check
|
||||||
return _flitter.view(res, 'dash_v1:out', {user: req.session.auth.user, out, prettyd:pretty, show_back: true, title: out.brief, title_small: true });
|
return _flitter.view(res, 'dash_v1:out', {project, user: req.session.auth.user, out, prettyd:pretty, show_back: true, title: out.brief, title_small: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
async out_delete(req, res, next){
|
async out_delete(req, res, next){
|
||||||
@ -264,6 +265,49 @@ class v1 {
|
|||||||
|
|
||||||
return res.redirect('/dash/v1')
|
return res.redirect('/dash/v1')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async project_share_invite(req, res, next){
|
||||||
|
const project = await Project.findById(req.params.id)
|
||||||
|
if ( !project ) return _flitter.error(res, 404, {reason: 'Project not found with the specified ID.'})
|
||||||
|
|
||||||
|
if ( !project.user_id === req.session.auth.uuid ) return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'})
|
||||||
|
|
||||||
|
let share_data = {
|
||||||
|
project_id: project.id,
|
||||||
|
by_user_id: req.session.auth.uuid,
|
||||||
|
created_on: Date.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
const share = new Invite(share_data)
|
||||||
|
await share.save()
|
||||||
|
|
||||||
|
return _flitter.view(res, 'dash_v1:invite', {share, project, title: 'Generate Invite Link', show_back: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
async accept_invite(req, res, next){
|
||||||
|
if ( !req.session.invite ) return res.redirect('/dash/v1')
|
||||||
|
|
||||||
|
const invite = await Invite.findById(req.session.invite_data.invite)
|
||||||
|
if ( !invite ) return _flitter.error(res, 404, {reason: 'This invitation is no longer valid. Sorry.'})
|
||||||
|
|
||||||
|
const project = await Project.findById(req.session.invite_data.project)
|
||||||
|
if ( !project ) return _flitter.error(res, 404, {reason: 'This project no longer exists.'})
|
||||||
|
|
||||||
|
const user = await _flitter.model('User').findById(req.session.invite_data.user)
|
||||||
|
if ( !user ) return _flitter.error(res, 404, {reason: 'This user no longer exists. Sorry.'})
|
||||||
|
|
||||||
|
if ( !project.shared_user_ids.includes(req.session.auth.uuid) && !(project.user_id === req.session.auth.uuid) ){
|
||||||
|
project.shared_user_ids.push(req.session.auth.uuid)
|
||||||
|
await project.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
invite.used = true
|
||||||
|
await invite.save()
|
||||||
|
|
||||||
|
req.session.invite = false
|
||||||
|
req.session.invite_data = false
|
||||||
|
return res.redirect('/dash/v1/project/view/'+project.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = exports = v1
|
module.exports = exports = v1
|
||||||
|
13
app/models/v1/Invite.model.js
Normal file
13
app/models/v1/Invite.model.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* Invite Model
|
||||||
|
* -------------------------------------------------------------
|
||||||
|
* Put some description here!
|
||||||
|
*/
|
||||||
|
const Invite = {
|
||||||
|
project_id: String,
|
||||||
|
by_user_id: String,
|
||||||
|
created_on: Date,
|
||||||
|
used: { type: Boolean, default: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = exports = Invite
|
@ -9,10 +9,10 @@
|
|||||||
* Route-specific middleware should be specified in the corresponding
|
* Route-specific middleware should be specified in the corresponding
|
||||||
* routes file.
|
* routes file.
|
||||||
*/
|
*/
|
||||||
const Middleware = [
|
let Middleware = [
|
||||||
|
|
||||||
'Debug',
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if ( _flitter.config('server.environment') === 'development' ) Middleware.push('Debug')
|
||||||
|
|
||||||
module.exports = exports = Middleware
|
module.exports = exports = Middleware
|
||||||
|
26
app/routing/middleware/v1/Invite.middleware.js
Normal file
26
app/routing/middleware/v1/Invite.middleware.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Invite Middleware
|
||||||
|
* -------------------------------------------------------------
|
||||||
|
* Put some description here!
|
||||||
|
*/
|
||||||
|
class Invite {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run the middleware test.
|
||||||
|
* This method is required by all Flitter middleware.
|
||||||
|
* It should either call the next function in the stack,
|
||||||
|
* or it should handle the response accordingly.
|
||||||
|
*/
|
||||||
|
test(req, res, next, args = {}){
|
||||||
|
if ( req.session.invite && !req.originalUrl.includes('/dash/v1/invitation/accept') ){
|
||||||
|
return res.redirect('/dash/v1/invitation/accept')
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call the next function in the stack.
|
||||||
|
*/
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Invite
|
@ -34,6 +34,8 @@ const v1 = {
|
|||||||
*/
|
*/
|
||||||
get: {
|
get: {
|
||||||
// '/': [ controller('Controller_Name').handler_name ],
|
// '/': [ controller('Controller_Name').handler_name ],
|
||||||
|
'/invitation/:id': [ _flitter.controller('api:v1').invite_show ],
|
||||||
|
'/invitation/:id/accept': [ _flitter.controller('api:v1').invite_accept ],
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -21,7 +21,8 @@ const v1 = {
|
|||||||
*/
|
*/
|
||||||
middleware: [
|
middleware: [
|
||||||
// mw('Middleware Name'),
|
// mw('Middleware Name'),
|
||||||
_flitter.mw('auth:RequireAuth')
|
_flitter.mw('auth:RequireAuth'),
|
||||||
|
_flitter.mw('v1:Invite')
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -44,11 +45,14 @@ const v1 = {
|
|||||||
'/project/share/:id/share/:user': [ _flitter.controller('dash:v1').project_share_do ],
|
'/project/share/:id/share/:user': [ _flitter.controller('dash:v1').project_share_do ],
|
||||||
'/project/share/:id/revoke/:user': [ _flitter.controller('dash:v1').project_share_revoke ],
|
'/project/share/:id/revoke/:user': [ _flitter.controller('dash:v1').project_share_revoke ],
|
||||||
'/project/share/:id/transfer/:user': [ _flitter.controller('dash:v1').project_share_transfer ],
|
'/project/share/:id/transfer/:user': [ _flitter.controller('dash:v1').project_share_transfer ],
|
||||||
|
'/project/share/:id/invite': [ _flitter.controller('dash:v1').project_share_invite ],
|
||||||
|
|
||||||
'/out/view/:id': [ _flitter.controller('dash:v1').out_view ],
|
'/out/view/:id': [ _flitter.controller('dash:v1').out_view ],
|
||||||
'/out/delete/:id/:project': [ _flitter.controller('dash:v1').out_delete ],
|
'/out/delete/:id/:project': [ _flitter.controller('dash:v1').out_delete ],
|
||||||
|
|
||||||
'/code': [ _flitter.controller('dash:v1').view_code ],
|
'/code': [ _flitter.controller('dash:v1').view_code ],
|
||||||
|
|
||||||
|
'/invitation/accept': [ _flitter.controller('dash:v1').accept_invite ],
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
19
app/views/dash_v1/accept.pug
Normal file
19
app/views/dash_v1/accept.pug
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
html
|
||||||
|
head
|
||||||
|
title Project Invitation | DevBug
|
||||||
|
link(rel='stylesheet' href='/assets/dash_v1.css')
|
||||||
|
script(src='/assets/dash_v1.js')
|
||||||
|
body
|
||||||
|
.page-header
|
||||||
|
.devbug-header DevBug | v#{devbug.version} #{(project ? " | Project: "+project.name+" | API: "+project.uuid : "")}
|
||||||
|
h1 Accept Invitation?
|
||||||
|
ul.navul
|
||||||
|
li.navli
|
||||||
|
a.nava(href='/dash/v1') Dashboard Login
|
||||||
|
.spacer
|
||||||
|
p You've been invited to view the debugging project "#{project.name}" by #{user.username}.
|
||||||
|
p To accept this invitation, you must have a DevBug account. You will be redirected to the registration portal.
|
||||||
|
a.btn(href='/api/v1/invitation/'+invite.id+'/accept') Accept
|
||||||
|
br
|
||||||
|
h3 What's DevBug?
|
||||||
|
p DevBug is a debugging output server used to help developers work more efficiently. Using inline-code clients, developers can output variables and data from their programs. This data is stored in a DevBug project, where it can easily be shared with others.
|
7
app/views/dash_v1/invite.pug
Normal file
7
app/views/dash_v1/invite.pug
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
extends ./template
|
||||||
|
block content
|
||||||
|
p You can send this link to someone without a DevBug account to invite them to register.
|
||||||
|
p Once they register, #{project.name} will be shared with them. This link can only be used once.
|
||||||
|
h3 Invitation Link
|
||||||
|
pre
|
||||||
|
code #{_flitter.config('server.url')+'/api/v1/invitation/'+share.id}
|
@ -24,6 +24,9 @@ block content
|
|||||||
strike Transfer Ownership
|
strike Transfer Ownership
|
||||||
br
|
br
|
||||||
h2 Share With New User
|
h2 Share With New User
|
||||||
|
a.btn(href='/dash/v1/project/share/'+project.id+'/invite') Generate Sharing Link
|
||||||
|
br
|
||||||
|
br
|
||||||
table
|
table
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
|
@ -10,7 +10,8 @@ const server_config = {
|
|||||||
},
|
},
|
||||||
uploads: {
|
uploads: {
|
||||||
destination: './uploads'
|
destination: './uploads'
|
||||||
}
|
},
|
||||||
|
url: process.env.SERVER_URL || "localhost",
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,4 +7,5 @@ DATABASE_NAME=flitter
|
|||||||
DATABASE_AUTH=false
|
DATABASE_AUTH=false
|
||||||
|
|
||||||
SECRET=changeme
|
SECRET=changeme
|
||||||
ENVIRONMENT=production
|
ENVIRONMENT=production
|
||||||
|
SERVER_URL=http://localhost
|
Loading…
Reference in New Issue
Block a user