fix webfinger routing, create group listing api, web homepage with groups list
This commit is contained in:
parent
5407d1cfe1
commit
45f25bdc6a
11 changed files with 115 additions and 146 deletions
6
index.js
6
index.js
|
@ -29,7 +29,11 @@ app.set('port', process.env.PORT || PORT)
|
|||
app.set('port-https', process.env.PORT_HTTPS || PORT_HTTPS)
|
||||
app.use(morgan('combined'))
|
||||
app.use(history({
|
||||
index: '/web/index.html'
|
||||
index: '/web/index.html',
|
||||
rewrites: [
|
||||
// do not redirect webfinger et c.
|
||||
{ from: /^\/\.well-known\//, to: context => context.request.originalUrl }
|
||||
]
|
||||
}))
|
||||
app.use(bodyParser.json({
|
||||
type: pub.consts.jsonldTypes
|
||||
|
|
13
package-lock.json
generated
13
package-lock.json
generated
|
@ -2645,8 +2645,7 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -3061,8 +3060,7 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -3118,7 +3116,6 @@
|
|||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -3162,14 +3159,12 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "guppe",
|
||||
"version": "0.0.1",
|
||||
"description": "Decentralized social groups with ActivityPub, NodeJS, Express, and Mongodb",
|
||||
"description": "Decentralized social groups with ActivityPub, NodeJS, Express, Vue, and Mongodb",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.18.3",
|
||||
|
|
|
@ -4,6 +4,30 @@ const router = express.Router()
|
|||
const pub = require('../pub')
|
||||
const net = require('../net')
|
||||
|
||||
// list active groups
|
||||
router.get('/', net.validators.jsonld, function (req, res) {
|
||||
const db = req.app.get('db')
|
||||
db.collection('streams')
|
||||
.aggregate([
|
||||
{ $limit: 10000 }, // don't traverse the entire history
|
||||
{ $match: { type: 'Announce' } },
|
||||
{ $group: { _id: '$actor', postCount: { $sum: 1 } } },
|
||||
{ $lookup: { from: 'objects', localField: '_id', foreignField: 'id', as: 'actor' } },
|
||||
// merge joined actor up
|
||||
{ $replaceRoot: { newRoot: { $mergeObjects: [{ $arrayElemAt: ['$actor', 0] }, '$$ROOT'] } } },
|
||||
{ $project: { postCount: 1, preferredUsername: 1 } }
|
||||
])
|
||||
.sort({ postCount: -1 })
|
||||
.limit(Number.parseInt(req.query.n) || 20)
|
||||
.toArray()
|
||||
.then(groups => { console.log(JSON.stringify(groups)); return groups })
|
||||
.then(groups => res.json(groups))
|
||||
.catch(err => {
|
||||
console.log(err.message)
|
||||
return res.status(500).send()
|
||||
})
|
||||
})
|
||||
|
||||
router.get('/:name', net.validators.jsonld, function (req, res) {
|
||||
const name = req.params.name
|
||||
if (!name) {
|
||||
|
|
47
web/package-lock.json
generated
47
web/package-lock.json
generated
|
@ -3026,8 +3026,7 @@
|
|||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"coa": {
|
||||
"version": "2.0.2",
|
||||
|
@ -5634,8 +5633,7 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -5656,14 +5654,12 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -5678,20 +5674,17 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -5808,8 +5801,7 @@
|
|||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -5821,7 +5813,6 @@
|
|||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -5836,7 +5827,6 @@
|
|||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
@ -5844,14 +5834,12 @@
|
|||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -5870,7 +5858,6 @@
|
|||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -5951,8 +5938,7 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -5964,7 +5950,6 @@
|
|||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -6050,8 +6035,7 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -6087,7 +6071,6 @@
|
|||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -6107,7 +6090,6 @@
|
|||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -6151,14 +6133,12 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -10214,8 +10194,7 @@
|
|||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
|
||||
"integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"rx-lite-aggregates": {
|
||||
"version": "4.0.8",
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
</div> -->
|
||||
<div class="w3-bar w3-black w3-card"> </div>
|
||||
<div class="w3-row">
|
||||
<div class="w3-col s0 m2"> </div>
|
||||
<div class="w3-col s12 m8 w3-content">
|
||||
<div class="w3-col s0 m1 l2"> </div>
|
||||
<div class="w3-col s12 m10 l8 w3-content">
|
||||
<router-view/>
|
||||
</div>
|
||||
<div class="w3-col s0 m2"> </div>
|
||||
<div class="w3-col s0 m1 l2"> </div>
|
||||
</div>
|
||||
<footer class="w3-container w3-padding-64 w3-center w3-opacity w3-light-grey w3-xlarge">
|
||||
<a href="https://github.com/wmurphyrd/guppe"><i class="fa fa-github w3-hover-opacity"></i></a>
|
||||
|
@ -23,7 +23,6 @@
|
|||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br>
|
||||
check out the
|
||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
||||
</p>
|
||||
<h3>Installed CLI Plugins</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-unit-mocha" target="_blank" rel="noopener">unit-mocha</a></li>
|
||||
</ul>
|
||||
<h3>Essential Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
||||
</ul>
|
||||
<h3>Ecosystem</h3>
|
||||
<ul>
|
||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HelloWorld',
|
||||
props: {
|
||||
msg: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
h3 {
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
|
@ -13,19 +13,11 @@ export default new Router({
|
|||
name: 'home',
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (about.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
|
||||
},
|
||||
{
|
||||
path: '/u/:name',
|
||||
name: 'profile',
|
||||
component: Profile,
|
||||
props: true,
|
||||
props: true
|
||||
}
|
||||
]
|
||||
})
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
|
@ -1,40 +1,75 @@
|
|||
<template>
|
||||
<div class="w3-container">
|
||||
<div class="w3-center">
|
||||
<img alt="Guppe logo" src="/f/guppe.png">
|
||||
<h1>Guppe Groups</h1>
|
||||
</div>
|
||||
<h1 class="w3-center">Guppe Groups</h1>
|
||||
<p>
|
||||
Guppe brings social groups to the fediverse — making it easy to connect and meet new
|
||||
people based on shared
|
||||
interests without the maniuplation of your attention to maximize ad revenue nor the
|
||||
walled garden lock-in of capitalist social media.
|
||||
Guppe brings social groups to the fediverse — making it easy to connect and meet new
|
||||
people based on shared
|
||||
interests without the maniuplation of your attention to maximize ad revenue nor the
|
||||
walled garden lock-in of capitalist social media.
|
||||
</p>
|
||||
<h2 class="w3-center">How does Guppe work?</h2>
|
||||
<p>
|
||||
Guppe groups look like regular users you can interact with using your existing account on any
|
||||
ActivityPub service, but they automatically share anything you send them with all of their followers.
|
||||
</p>
|
||||
<h2>How does Guppe work?</h2>
|
||||
<ol>
|
||||
<li>Follow a user on @gup.pe to join that group</li>
|
||||
<li>Mention a user on @gup.pe to share a post with everyone in the group</li>
|
||||
<li>New groups are created on demand, just search for @YourGroupNameHere@gup.pe and it will show up</li>
|
||||
<li>Visit a @gup.pe user profile to see the group history</li>
|
||||
<li>Follow a group on @gup.pe to join that group</li>
|
||||
<li>Mention a group on @gup.pe to share a post with everyone in the group</li>
|
||||
<li>New groups are created on demand, just search for or mention @YourGroupNameHere@gup.pe and it will show up</li>
|
||||
<li>Visit a @gup.pe group profile to see the group history</li>
|
||||
</ol>
|
||||
<p>
|
||||
A Guppe Group has its own account and profile.
|
||||
This server-2-server ActivityPub implementation adds decentralized,
|
||||
federaded "groups" support across all ActivityPub compliant social media networks.
|
||||
Users join groups by following group-type actors on Guppe servers and contribute t
|
||||
groups by mentioning those same actors in a post. Guppe group actors will
|
||||
automatically forward posts they receive to all group members so that everyone in the
|
||||
group sees any post made to the group. Guppe group actors' profiles (e.g. outboxes) also
|
||||
serve as a group discussion history.
|
||||
</p>
|
||||
<h2 class="w3-center">Active Groups</h2>
|
||||
<div class="profile-grid w3-margin-bottom w3-mobile">
|
||||
<div v-for="group of groups" class="w3-card" :key="group._id">
|
||||
<Profile :name="group.preferredUsername" :post-limit="3"
|
||||
class="profile"/>
|
||||
<router-link :to="`/u/${group.preferredUsername}`">More...</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// @ is an alias to /src
|
||||
// import HelloWorld from '@/components/HelloWorld.vue'
|
||||
import Profile from '@/views/Profile.vue'
|
||||
|
||||
export default {
|
||||
name: 'home',
|
||||
components: {
|
||||
Profile
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
groups: [],
|
||||
error: null
|
||||
}
|
||||
},
|
||||
created () {
|
||||
window.fetch(`/u/`, {
|
||||
method: 'get',
|
||||
headers: {
|
||||
accept: 'application/json'
|
||||
}
|
||||
}).then(res => res.json())
|
||||
.then(json => {
|
||||
this.groups = json
|
||||
})
|
||||
.catch(err => this.error = err.message)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.profile {
|
||||
width: 300px;
|
||||
}
|
||||
.profile-grid {
|
||||
display: grid;
|
||||
grid-gap: 15px;
|
||||
grid-template-columns: auto auto;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="w3-container w3-content w3-center w3-padding-32">
|
||||
<img class="profile-main" :src="groupProfileSrc">
|
||||
<img class="w3-image" :src="groupProfileSrc">
|
||||
<h2 class="w3-wide">{{ actor.preferredUsername }}</h2>
|
||||
<p class="w3-opacity"><i>{{ actor.summary }}</i></p>
|
||||
<p class="w3-left-align">To join {{ actor.preferredUsername }}, enter your handle below and you'll be
|
||||
|
@ -43,6 +43,10 @@ export default {
|
|||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
postLimit: {
|
||||
type: Number,
|
||||
default: 20
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
@ -124,6 +128,7 @@ export default {
|
|||
.then(outbox => {
|
||||
this.stream = outbox.orderedItems // || fetch page
|
||||
.filter(act => act.type === 'Announce')
|
||||
.slice(0, this.postLimit)
|
||||
})
|
||||
.catch(err => this.error = err.message)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue