Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions internal/db/posts/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
package models

import "time"

// this struct will be used in internal packages
// for calling Post interface methods.
type Post struct {
Id string `bson:"_id,omitempty"`
UserId int `bson:"userId,omitempty"`
AuthorName string `bson:"authorName,omitempty"`
CreatedAt time.Time `bson:"createdAt,omitempty"`
CityId int `bson:"cityId,omitempty"`
CountryId int `bson:"countryId,omitempty"`
Coordinates Point `bson:"coordinates,omitempty"`
IsActive bool `bson:"isActive,omitempty"`
LikesCount int `bson:"likesCount,omitempty"`
CommentsCount int `bson:"commentsCount,omitempty"`
Content Content `bson:"content,omitempty,inline"`
}

type Point struct {
Type string `bson:"type"` //needs to be set to Point
Coordinates []float64 `bson:"coordinates"` //[long,lat]
}

type Content struct {
Text string `bson:"text,omitempty"`
BackgroundUrl string `bson:"backgroundUrl,omitempty"`
BackgroundType BackgroundType `bson:"backgroundType"`
}

type BackgroundType int

const (
PreloadedImage BackgroundType = iota
PreloadedVideo
CustomImage
CustomVideo
)
13 changes: 13 additions & 0 deletions internal/db/posts/posts.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db/posts/models"
)

type Posts interface {
Create(context.Context, models.Post) error
FindNearby(context.Context, float64, float64) ([]models.Post, error)
Like(context.Context, string) error
}
96 changes: 96 additions & 0 deletions internal/db/posts/posts_repository.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db"
"github.com/TheDevExperiment/server/internal/db/posts/models"
"github.com/TheDevExperiment/server/internal/log"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type PostsRepository struct {
collection *mongo.Collection
}

var singletonPostCollection *mongo.Collection

const defaultNearbySearchRadius = 1000 // 1km
const defaultPageLimit = 10

func NewPostsRepository() *PostsRepository {
// use mongodb to find collection here
if singletonPostCollection == nil {
log.Debug("Created connection to post collection")
singletonPostCollection = db.GetCollection(viper.GetString("mongodb.db_name"), "posts")
}
return &PostsRepository{collection: singletonPostCollection}
}

func (pr *PostsRepository) Create(ctx context.Context, post models.Post) error {
log.Debugf("Received new post create request data: %+v", post)
res, err := pr.collection.InsertOne(ctx, post)
log.Debugf("Response received from mongo: %+v", res)
return err
}

func prepareNearbySortQuery(latitude, longitude float64) primitive.M {
query := bson.M{
"coordinates": bson.M{
"$near": bson.M{
"$geometry": bson.M{
"type": "Point",
"coordinates": []float64{longitude, latitude},
},
"$maxDistance": defaultNearbySearchRadius,
},
},
}
return query
}

func preparePaginatedNearbySortOpts(page int) *options.FindOptions {
findOptions := options.Find()
findOptions.SetLimit(int64(defaultPageLimit)) // Set the limit to the specified value
findOptions.SetSkip(int64((page - 1) * defaultPageLimit)) // Skip records based on the page number and limit
findOptions.SetSort(bson.D{{Key: "_id", Value: -1}}) // Sort by "_id" field in descending order
findOptions.SetProjection(bson.M{"coordinates": 0}) // Exclude "coordinates" field from the result
return findOptions
}

func (pr *PostsRepository) FindNearby(ctx context.Context, lat, long float64, page int) ([]models.Post, error) {
log.Debugf("Received new nearby req data: %f %f", lat, long)
query := prepareNearbySortQuery(lat, long)
optsWithPagination := preparePaginatedNearbySortOpts(page)

cur, err := pr.collection.Find(ctx, query, optsWithPagination)
if err != nil {
return nil, err
}
defer cur.Close(ctx)

var posts []models.Post
for cur.Next(ctx) {
var post models.Post
err := cur.Decode(&post)
if err != nil {
log.Errorf("Error while parsing doc: %s", err.Error())
continue
}
posts = append(posts, post)
}

if err := cur.Err(); err != nil {
return nil, err
}

return posts, nil
}

func (pr *PostsRepository) Like(context.Context, string) error {
return nil
}
54 changes: 54 additions & 0 deletions internal/post/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
package post

import (
"net/http"

"github.com/TheDevExperiment/server/internal/db/posts"
"github.com/TheDevExperiment/server/router/models"
"github.com/gin-gonic/gin"
)

func CreateV1(c *gin.Context) {
// first bind the req to our model
var req models.CreatePostRequest
var res models.CreatePostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

err := pr.Create(c, req.Post)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}
res.Message = "Created new post"
c.JSON(http.StatusOK, res)
}

func NearbyV1(c *gin.Context) {
// first bind the req to our model
var req models.NearbyPostRequest
var res models.NearbyPostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

posts, err := pr.FindNearby(c, req.Latitude, req.Longitude, req.Page)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}

res.Posts = posts
c.JSON(http.StatusOK, res)
}
57 changes: 21 additions & 36 deletions mongo-seed/posts.json
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,26 @@
[
{
"_id": {"$oid": "6170c708b210f8e6e31d6c94"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c91"},
"authorName": "John Doe",
"createdAt": {"$date": "2022-10-22T14:30:00.000Z"},
"content": {
"message": "hello world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "New York",
"countryId": "USA",
"coordinate": {
{
"_id": "6061f072c1760a272b4bb49a",
"userId": 12345,
"authorName": "John Doe",
"createdAt": "2023-04-10T10:30:00Z",
"cityId": 1,
"countryId": 2,
"coordinates": {
"type": "Point",
"coordinates": [-73.935242, 40.730610]
},
"isActive": true,
"likes": 5
"coordinates": [
-122.4194,
37.7749
]
},
{
"_id": {"$oid": "6170c708b210f8e6e31d6c95"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c92"},
"authorName": "Jane Smith",
"createdAt": {"$date": "2022-10-23T16:30:00.000Z"},
"content": {
"message": "bye world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "Toronto",
"countryId": "Canada",
"coordinate": {
"type": "Point",
"coordinates": [-79.3832, 43.6532]
},
"isActive": true,
"likes": 10
"isActive": true,
"likesCount": 10,
"commentsCount": 5,
"content": {
"text": "Lorem ipsum dolor sit amet",
"backgroundUrl": "https://example.com/image.jpg",
"backgroundType": 0
}
]
}
]

23 changes: 23 additions & 0 deletions router/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
package models

import "github.com/TheDevExperiment/server/internal/db/posts/models"

type CreatePostRequest struct {
Post models.Post `json:"post" binding:"required"`
}

type CreatePostResponse struct {
Message string `form:"message" json:"message" binding:"required"`
}

type NearbyPostRequest struct {
Latitude float64 `json:"latitude" binding:"required"`
Longitude float64 `json:"longitude" binding:"required"`
Page int ` json:"page"`
// Radius int -> if app wants to support tweakable filter
}

type NearbyPostResponse struct {
Posts []models.Post `json:"posts" binding:"required"`
Message string
}
3 changes: 3 additions & 0 deletions router/router.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@ package router
import (
"github.com/TheDevExperiment/server/internal/auth"
"github.com/TheDevExperiment/server/internal/db/repositories"
"github.com/TheDevExperiment/server/internal/post"
"github.com/gin-gonic/gin"
)

Expand All@@ -27,5 +28,7 @@ func SetupRouter() *gin.Engine {

// define all the routes
r.POST("/auth/v1/guest-validate", auth.GuestValidateV1)
r.POST("/post/v1/create", post.CreateV1)
r.POST("/post/v1/nearby", post.NearbyV1)
return r
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions internal/db/posts/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
package models

import "time"

// this struct will be used in internal packages
// for calling Post interface methods.
type Post struct {
Id string `bson:"_id,omitempty"`
UserId int `bson:"userId,omitempty"`
AuthorName string `bson:"authorName,omitempty"`
CreatedAt time.Time `bson:"createdAt,omitempty"`
CityId int `bson:"cityId,omitempty"`
CountryId int `bson:"countryId,omitempty"`
Coordinates Point `bson:"coordinates,omitempty"`
IsActive bool `bson:"isActive,omitempty"`
LikesCount int `bson:"likesCount,omitempty"`
CommentsCount int `bson:"commentsCount,omitempty"`
Content Content `bson:"content,omitempty,inline"`
}

type Point struct {
Type string `bson:"type"` //needs to be set to Point
Coordinates []float64 `bson:"coordinates"` //[long,lat]
}

type Content struct {
Text string `bson:"text,omitempty"`
BackgroundUrl string `bson:"backgroundUrl,omitempty"`
BackgroundType BackgroundType `bson:"backgroundType"`
}

type BackgroundType int

const (
PreloadedImage BackgroundType = iota
PreloadedVideo
CustomImage
CustomVideo
)
13 changes: 13 additions & 0 deletions internal/db/posts/posts.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db/posts/models"
)

type Posts interface {
Create(context.Context, models.Post) error
FindNearby(context.Context, float64, float64) ([]models.Post, error)
Like(context.Context, string) error
}
96 changes: 96 additions & 0 deletions internal/db/posts/posts_repository.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db"
"github.com/TheDevExperiment/server/internal/db/posts/models"
"github.com/TheDevExperiment/server/internal/log"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type PostsRepository struct {
collection *mongo.Collection
}

var singletonPostCollection *mongo.Collection

const defaultNearbySearchRadius = 1000 // 1km
const defaultPageLimit = 10

func NewPostsRepository() *PostsRepository {
// use mongodb to find collection here
if singletonPostCollection == nil {
log.Debug("Created connection to post collection")
singletonPostCollection = db.GetCollection(viper.GetString("mongodb.db_name"), "posts")
}
return &PostsRepository{collection: singletonPostCollection}
}

func (pr *PostsRepository) Create(ctx context.Context, post models.Post) error {
log.Debugf("Received new post create request data: %+v", post)
res, err := pr.collection.InsertOne(ctx, post)
log.Debugf("Response received from mongo: %+v", res)
return err
}

func prepareNearbySortQuery(latitude, longitude float64) primitive.M {
query := bson.M{
"coordinates": bson.M{
"$near": bson.M{
"$geometry": bson.M{
"type": "Point",
"coordinates": []float64{longitude, latitude},
},
"$maxDistance": defaultNearbySearchRadius,
},
},
}
return query
}

func preparePaginatedNearbySortOpts(page int) *options.FindOptions {
findOptions := options.Find()
findOptions.SetLimit(int64(defaultPageLimit)) // Set the limit to the specified value
findOptions.SetSkip(int64((page - 1) * defaultPageLimit)) // Skip records based on the page number and limit
findOptions.SetSort(bson.D{{Key: "_id", Value: -1}}) // Sort by "_id" field in descending order
findOptions.SetProjection(bson.M{"coordinates": 0}) // Exclude "coordinates" field from the result
return findOptions
}

func (pr *PostsRepository) FindNearby(ctx context.Context, lat, long float64, page int) ([]models.Post, error) {
log.Debugf("Received new nearby req data: %f %f", lat, long)
query := prepareNearbySortQuery(lat, long)
optsWithPagination := preparePaginatedNearbySortOpts(page)

cur, err := pr.collection.Find(ctx, query, optsWithPagination)
if err != nil {
return nil, err
}
defer cur.Close(ctx)

var posts []models.Post
for cur.Next(ctx) {
var post models.Post
err := cur.Decode(&post)
if err != nil {
log.Errorf("Error while parsing doc: %s", err.Error())
continue
}
posts = append(posts, post)
}

if err := cur.Err(); err != nil {
return nil, err
}

return posts, nil
}

func (pr *PostsRepository) Like(context.Context, string) error {
return nil
}
54 changes: 54 additions & 0 deletions internal/post/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
package post

import (
"net/http"

"github.com/TheDevExperiment/server/internal/db/posts"
"github.com/TheDevExperiment/server/router/models"
"github.com/gin-gonic/gin"
)

func CreateV1(c *gin.Context) {
// first bind the req to our model
var req models.CreatePostRequest
var res models.CreatePostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

err := pr.Create(c, req.Post)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}
res.Message = "Created new post"
c.JSON(http.StatusOK, res)
}

func NearbyV1(c *gin.Context) {
// first bind the req to our model
var req models.NearbyPostRequest
var res models.NearbyPostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

posts, err := pr.FindNearby(c, req.Latitude, req.Longitude, req.Page)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}

res.Posts = posts
c.JSON(http.StatusOK, res)
}
57 changes: 21 additions & 36 deletions mongo-seed/posts.json
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,26 @@
[
{
"_id": {"$oid": "6170c708b210f8e6e31d6c94"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c91"},
"authorName": "John Doe",
"createdAt": {"$date": "2022-10-22T14:30:00.000Z"},
"content": {
"message": "hello world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "New York",
"countryId": "USA",
"coordinate": {
{
"_id": "6061f072c1760a272b4bb49a",
"userId": 12345,
"authorName": "John Doe",
"createdAt": "2023-04-10T10:30:00Z",
"cityId": 1,
"countryId": 2,
"coordinates": {
"type": "Point",
"coordinates": [-73.935242, 40.730610]
},
"isActive": true,
"likes": 5
"coordinates": [
-122.4194,
37.7749
]
},
{
"_id": {"$oid": "6170c708b210f8e6e31d6c95"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c92"},
"authorName": "Jane Smith",
"createdAt": {"$date": "2022-10-23T16:30:00.000Z"},
"content": {
"message": "bye world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "Toronto",
"countryId": "Canada",
"coordinate": {
"type": "Point",
"coordinates": [-79.3832, 43.6532]
},
"isActive": true,
"likes": 10
"isActive": true,
"likesCount": 10,
"commentsCount": 5,
"content": {
"text": "Lorem ipsum dolor sit amet",
"backgroundUrl": "https://example.com/image.jpg",
"backgroundType": 0
}
]
}
]

23 changes: 23 additions & 0 deletions router/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
package models

import "github.com/TheDevExperiment/server/internal/db/posts/models"

type CreatePostRequest struct {
Post models.Post `json:"post" binding:"required"`
}

type CreatePostResponse struct {
Message string `form:"message" json:"message" binding:"required"`
}

type NearbyPostRequest struct {
Latitude float64 `json:"latitude" binding:"required"`
Longitude float64 `json:"longitude" binding:"required"`
Page int ` json:"page"`
// Radius int -> if app wants to support tweakable filter
}

type NearbyPostResponse struct {
Posts []models.Post `json:"posts" binding:"required"`
Message string
}
3 changes: 3 additions & 0 deletions router/router.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@ package router
import (
"github.com/TheDevExperiment/server/internal/auth"
"github.com/TheDevExperiment/server/internal/db/repositories"
"github.com/TheDevExperiment/server/internal/post"
"github.com/gin-gonic/gin"
)

Expand All@@ -27,5 +28,7 @@ func SetupRouter() *gin.Engine {

// define all the routes
r.POST("/auth/v1/guest-validate", auth.GuestValidateV1)
r.POST("/post/v1/create", post.CreateV1)
r.POST("/post/v1/nearby", post.NearbyV1)
return r
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions internal/db/posts/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
package models

import "time"

// this struct will be used in internal packages
// for calling Post interface methods.
type Post struct {
Id string `bson:"_id,omitempty"`
UserId int `bson:"userId,omitempty"`
AuthorName string `bson:"authorName,omitempty"`
CreatedAt time.Time `bson:"createdAt,omitempty"`
CityId int `bson:"cityId,omitempty"`
CountryId int `bson:"countryId,omitempty"`
Coordinates Point `bson:"coordinates,omitempty"`
IsActive bool `bson:"isActive,omitempty"`
LikesCount int `bson:"likesCount,omitempty"`
CommentsCount int `bson:"commentsCount,omitempty"`
Content Content `bson:"content,omitempty,inline"`
}

type Point struct {
Type string `bson:"type"` //needs to be set to Point
Coordinates []float64 `bson:"coordinates"` //[long,lat]
}

type Content struct {
Text string `bson:"text,omitempty"`
BackgroundUrl string `bson:"backgroundUrl,omitempty"`
BackgroundType BackgroundType `bson:"backgroundType"`
}

type BackgroundType int

const (
PreloadedImage BackgroundType = iota
PreloadedVideo
CustomImage
CustomVideo
)
13 changes: 13 additions & 0 deletions internal/db/posts/posts.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db/posts/models"
)

type Posts interface {
Create(context.Context, models.Post) error
FindNearby(context.Context, float64, float64) ([]models.Post, error)
Like(context.Context, string) error
}
96 changes: 96 additions & 0 deletions internal/db/posts/posts_repository.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db"
"github.com/TheDevExperiment/server/internal/db/posts/models"
"github.com/TheDevExperiment/server/internal/log"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type PostsRepository struct {
collection *mongo.Collection
}

var singletonPostCollection *mongo.Collection

const defaultNearbySearchRadius = 1000 // 1km
const defaultPageLimit = 10

func NewPostsRepository() *PostsRepository {
// use mongodb to find collection here
if singletonPostCollection == nil {
log.Debug("Created connection to post collection")
singletonPostCollection = db.GetCollection(viper.GetString("mongodb.db_name"), "posts")
}
return &PostsRepository{collection: singletonPostCollection}
}

func (pr *PostsRepository) Create(ctx context.Context, post models.Post) error {
log.Debugf("Received new post create request data: %+v", post)
res, err := pr.collection.InsertOne(ctx, post)
log.Debugf("Response received from mongo: %+v", res)
return err
}

func prepareNearbySortQuery(latitude, longitude float64) primitive.M {
query := bson.M{
"coordinates": bson.M{
"$near": bson.M{
"$geometry": bson.M{
"type": "Point",
"coordinates": []float64{longitude, latitude},
},
"$maxDistance": defaultNearbySearchRadius,
},
},
}
return query
}

func preparePaginatedNearbySortOpts(page int) *options.FindOptions {
findOptions := options.Find()
findOptions.SetLimit(int64(defaultPageLimit)) // Set the limit to the specified value
findOptions.SetSkip(int64((page - 1) * defaultPageLimit)) // Skip records based on the page number and limit
findOptions.SetSort(bson.D{{Key: "_id", Value: -1}}) // Sort by "_id" field in descending order
findOptions.SetProjection(bson.M{"coordinates": 0}) // Exclude "coordinates" field from the result
return findOptions
}

func (pr *PostsRepository) FindNearby(ctx context.Context, lat, long float64, page int) ([]models.Post, error) {
log.Debugf("Received new nearby req data: %f %f", lat, long)
query := prepareNearbySortQuery(lat, long)
optsWithPagination := preparePaginatedNearbySortOpts(page)

cur, err := pr.collection.Find(ctx, query, optsWithPagination)
if err != nil {
return nil, err
}
defer cur.Close(ctx)

var posts []models.Post
for cur.Next(ctx) {
var post models.Post
err := cur.Decode(&post)
if err != nil {
log.Errorf("Error while parsing doc: %s", err.Error())
continue
}
posts = append(posts, post)
}

if err := cur.Err(); err != nil {
return nil, err
}

return posts, nil
}

func (pr *PostsRepository) Like(context.Context, string) error {
return nil
}
54 changes: 54 additions & 0 deletions internal/post/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
package post

import (
"net/http"

"github.com/TheDevExperiment/server/internal/db/posts"
"github.com/TheDevExperiment/server/router/models"
"github.com/gin-gonic/gin"
)

func CreateV1(c *gin.Context) {
// first bind the req to our model
var req models.CreatePostRequest
var res models.CreatePostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

err := pr.Create(c, req.Post)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}
res.Message = "Created new post"
c.JSON(http.StatusOK, res)
}

func NearbyV1(c *gin.Context) {
// first bind the req to our model
var req models.NearbyPostRequest
var res models.NearbyPostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

posts, err := pr.FindNearby(c, req.Latitude, req.Longitude, req.Page)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}

res.Posts = posts
c.JSON(http.StatusOK, res)
}
57 changes: 21 additions & 36 deletions mongo-seed/posts.json
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,26 @@
[
{
"_id": {"$oid": "6170c708b210f8e6e31d6c94"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c91"},
"authorName": "John Doe",
"createdAt": {"$date": "2022-10-22T14:30:00.000Z"},
"content": {
"message": "hello world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "New York",
"countryId": "USA",
"coordinate": {
{
"_id": "6061f072c1760a272b4bb49a",
"userId": 12345,
"authorName": "John Doe",
"createdAt": "2023-04-10T10:30:00Z",
"cityId": 1,
"countryId": 2,
"coordinates": {
"type": "Point",
"coordinates": [-73.935242, 40.730610]
},
"isActive": true,
"likes": 5
"coordinates": [
-122.4194,
37.7749
]
},
{
"_id": {"$oid": "6170c708b210f8e6e31d6c95"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c92"},
"authorName": "Jane Smith",
"createdAt": {"$date": "2022-10-23T16:30:00.000Z"},
"content": {
"message": "bye world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "Toronto",
"countryId": "Canada",
"coordinate": {
"type": "Point",
"coordinates": [-79.3832, 43.6532]
},
"isActive": true,
"likes": 10
"isActive": true,
"likesCount": 10,
"commentsCount": 5,
"content": {
"text": "Lorem ipsum dolor sit amet",
"backgroundUrl": "https://example.com/image.jpg",
"backgroundType": 0
}
]
}
]

23 changes: 23 additions & 0 deletions router/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
package models

import "github.com/TheDevExperiment/server/internal/db/posts/models"

type CreatePostRequest struct {
Post models.Post `json:"post" binding:"required"`
}

type CreatePostResponse struct {
Message string `form:"message" json:"message" binding:"required"`
}

type NearbyPostRequest struct {
Latitude float64 `json:"latitude" binding:"required"`
Longitude float64 `json:"longitude" binding:"required"`
Page int ` json:"page"`
// Radius int -> if app wants to support tweakable filter
}

type NearbyPostResponse struct {
Posts []models.Post `json:"posts" binding:"required"`
Message string
}
3 changes: 3 additions & 0 deletions router/router.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@ package router
import (
"github.com/TheDevExperiment/server/internal/auth"
"github.com/TheDevExperiment/server/internal/db/repositories"
"github.com/TheDevExperiment/server/internal/post"
"github.com/gin-gonic/gin"
)

Expand All@@ -27,5 +28,7 @@ func SetupRouter() *gin.Engine {

// define all the routes
r.POST("/auth/v1/guest-validate", auth.GuestValidateV1)
r.POST("/post/v1/create", post.CreateV1)
r.POST("/post/v1/nearby", post.NearbyV1)
return r
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions internal/db/posts/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
package models

import "time"

// this struct will be used in internal packages
// for calling Post interface methods.
type Post struct {
Id string `bson:"_id,omitempty"`
UserId int `bson:"userId,omitempty"`
AuthorName string `bson:"authorName,omitempty"`
CreatedAt time.Time `bson:"createdAt,omitempty"`
CityId int `bson:"cityId,omitempty"`
CountryId int `bson:"countryId,omitempty"`
Coordinates Point `bson:"coordinates,omitempty"`
IsActive bool `bson:"isActive,omitempty"`
LikesCount int `bson:"likesCount,omitempty"`
CommentsCount int `bson:"commentsCount,omitempty"`
Content Content `bson:"content,omitempty,inline"`
}

type Point struct {
Type string `bson:"type"` //needs to be set to Point
Coordinates []float64 `bson:"coordinates"` //[long,lat]
}

type Content struct {
Text string `bson:"text,omitempty"`
BackgroundUrl string `bson:"backgroundUrl,omitempty"`
BackgroundType BackgroundType `bson:"backgroundType"`
}

type BackgroundType int

const (
PreloadedImage BackgroundType = iota
PreloadedVideo
CustomImage
CustomVideo
)
13 changes: 13 additions & 0 deletions internal/db/posts/posts.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db/posts/models"
)

type Posts interface {
Create(context.Context, models.Post) error
FindNearby(context.Context, float64, float64) ([]models.Post, error)
Like(context.Context, string) error
}
96 changes: 96 additions & 0 deletions internal/db/posts/posts_repository.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db"
"github.com/TheDevExperiment/server/internal/db/posts/models"
"github.com/TheDevExperiment/server/internal/log"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type PostsRepository struct {
collection *mongo.Collection
}

var singletonPostCollection *mongo.Collection

const defaultNearbySearchRadius = 1000 // 1km
const defaultPageLimit = 10

func NewPostsRepository() *PostsRepository {
// use mongodb to find collection here
if singletonPostCollection == nil {
log.Debug("Created connection to post collection")
singletonPostCollection = db.GetCollection(viper.GetString("mongodb.db_name"), "posts")
}
return &PostsRepository{collection: singletonPostCollection}
}

func (pr *PostsRepository) Create(ctx context.Context, post models.Post) error {
log.Debugf("Received new post create request data: %+v", post)
res, err := pr.collection.InsertOne(ctx, post)
log.Debugf("Response received from mongo: %+v", res)
return err
}

func prepareNearbySortQuery(latitude, longitude float64) primitive.M {
query := bson.M{
"coordinates": bson.M{
"$near": bson.M{
"$geometry": bson.M{
"type": "Point",
"coordinates": []float64{longitude, latitude},
},
"$maxDistance": defaultNearbySearchRadius,
},
},
}
return query
}

func preparePaginatedNearbySortOpts(page int) *options.FindOptions {
findOptions := options.Find()
findOptions.SetLimit(int64(defaultPageLimit)) // Set the limit to the specified value
findOptions.SetSkip(int64((page - 1) * defaultPageLimit)) // Skip records based on the page number and limit
findOptions.SetSort(bson.D{{Key: "_id", Value: -1}}) // Sort by "_id" field in descending order
findOptions.SetProjection(bson.M{"coordinates": 0}) // Exclude "coordinates" field from the result
return findOptions
}

func (pr *PostsRepository) FindNearby(ctx context.Context, lat, long float64, page int) ([]models.Post, error) {
log.Debugf("Received new nearby req data: %f %f", lat, long)
query := prepareNearbySortQuery(lat, long)
optsWithPagination := preparePaginatedNearbySortOpts(page)

cur, err := pr.collection.Find(ctx, query, optsWithPagination)
if err != nil {
return nil, err
}
defer cur.Close(ctx)

var posts []models.Post
for cur.Next(ctx) {
var post models.Post
err := cur.Decode(&post)
if err != nil {
log.Errorf("Error while parsing doc: %s", err.Error())
continue
}
posts = append(posts, post)
}

if err := cur.Err(); err != nil {
return nil, err
}

return posts, nil
}

func (pr *PostsRepository) Like(context.Context, string) error {
return nil
}
54 changes: 54 additions & 0 deletions internal/post/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
package post

import (
"net/http"

"github.com/TheDevExperiment/server/internal/db/posts"
"github.com/TheDevExperiment/server/router/models"
"github.com/gin-gonic/gin"
)

func CreateV1(c *gin.Context) {
// first bind the req to our model
var req models.CreatePostRequest
var res models.CreatePostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

err := pr.Create(c, req.Post)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}
res.Message = "Created new post"
c.JSON(http.StatusOK, res)
}

func NearbyV1(c *gin.Context) {
// first bind the req to our model
var req models.NearbyPostRequest
var res models.NearbyPostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

posts, err := pr.FindNearby(c, req.Latitude, req.Longitude, req.Page)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}

res.Posts = posts
c.JSON(http.StatusOK, res)
}
57 changes: 21 additions & 36 deletions mongo-seed/posts.json
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,26 @@
[
{
"_id": {"$oid": "6170c708b210f8e6e31d6c94"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c91"},
"authorName": "John Doe",
"createdAt": {"$date": "2022-10-22T14:30:00.000Z"},
"content": {
"message": "hello world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "New York",
"countryId": "USA",
"coordinate": {
{
"_id": "6061f072c1760a272b4bb49a",
"userId": 12345,
"authorName": "John Doe",
"createdAt": "2023-04-10T10:30:00Z",
"cityId": 1,
"countryId": 2,
"coordinates": {
"type": "Point",
"coordinates": [-73.935242, 40.730610]
},
"isActive": true,
"likes": 5
"coordinates": [
-122.4194,
37.7749
]
},
{
"_id": {"$oid": "6170c708b210f8e6e31d6c95"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c92"},
"authorName": "Jane Smith",
"createdAt": {"$date": "2022-10-23T16:30:00.000Z"},
"content": {
"message": "bye world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "Toronto",
"countryId": "Canada",
"coordinate": {
"type": "Point",
"coordinates": [-79.3832, 43.6532]
},
"isActive": true,
"likes": 10
"isActive": true,
"likesCount": 10,
"commentsCount": 5,
"content": {
"text": "Lorem ipsum dolor sit amet",
"backgroundUrl": "https://example.com/image.jpg",
"backgroundType": 0
}
]
}
]

23 changes: 23 additions & 0 deletions router/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
package models

import "github.com/TheDevExperiment/server/internal/db/posts/models"

type CreatePostRequest struct {
Post models.Post `json:"post" binding:"required"`
}

type CreatePostResponse struct {
Message string `form:"message" json:"message" binding:"required"`
}

type NearbyPostRequest struct {
Latitude float64 `json:"latitude" binding:"required"`
Longitude float64 `json:"longitude" binding:"required"`
Page int ` json:"page"`
// Radius int -> if app wants to support tweakable filter
}

type NearbyPostResponse struct {
Posts []models.Post `json:"posts" binding:"required"`
Message string
}
3 changes: 3 additions & 0 deletions router/router.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@ package router
import (
"github.com/TheDevExperiment/server/internal/auth"
"github.com/TheDevExperiment/server/internal/db/repositories"
"github.com/TheDevExperiment/server/internal/post"
"github.com/gin-gonic/gin"
)

Expand All@@ -27,5 +28,7 @@ func SetupRouter() *gin.Engine {

// define all the routes
r.POST("/auth/v1/guest-validate", auth.GuestValidateV1)
r.POST("/post/v1/create", post.CreateV1)
r.POST("/post/v1/nearby", post.NearbyV1)
return r
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions internal/db/posts/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
package models

import "time"

// this struct will be used in internal packages
// for calling Post interface methods.
type Post struct {
Id string `bson:"_id,omitempty"`
UserId int `bson:"userId,omitempty"`
AuthorName string `bson:"authorName,omitempty"`
CreatedAt time.Time `bson:"createdAt,omitempty"`
CityId int `bson:"cityId,omitempty"`
CountryId int `bson:"countryId,omitempty"`
Coordinates Point `bson:"coordinates,omitempty"`
IsActive bool `bson:"isActive,omitempty"`
LikesCount int `bson:"likesCount,omitempty"`
CommentsCount int `bson:"commentsCount,omitempty"`
Content Content `bson:"content,omitempty,inline"`
}

type Point struct {
Type string `bson:"type"` //needs to be set to Point
Coordinates []float64 `bson:"coordinates"` //[long,lat]
}

type Content struct {
Text string `bson:"text,omitempty"`
BackgroundUrl string `bson:"backgroundUrl,omitempty"`
BackgroundType BackgroundType `bson:"backgroundType"`
}

type BackgroundType int

const (
PreloadedImage BackgroundType = iota
PreloadedVideo
CustomImage
CustomVideo
)
13 changes: 13 additions & 0 deletions internal/db/posts/posts.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db/posts/models"
)

type Posts interface {
Create(context.Context, models.Post) error
FindNearby(context.Context, float64, float64) ([]models.Post, error)
Like(context.Context, string) error
}
96 changes: 96 additions & 0 deletions internal/db/posts/posts_repository.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db"
"github.com/TheDevExperiment/server/internal/db/posts/models"
"github.com/TheDevExperiment/server/internal/log"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type PostsRepository struct {
collection *mongo.Collection
}

var singletonPostCollection *mongo.Collection

const defaultNearbySearchRadius = 1000 // 1km
const defaultPageLimit = 10

func NewPostsRepository() *PostsRepository {
// use mongodb to find collection here
if singletonPostCollection == nil {
log.Debug("Created connection to post collection")
singletonPostCollection = db.GetCollection(viper.GetString("mongodb.db_name"), "posts")
}
return &PostsRepository{collection: singletonPostCollection}
}

func (pr *PostsRepository) Create(ctx context.Context, post models.Post) error {
log.Debugf("Received new post create request data: %+v", post)
res, err := pr.collection.InsertOne(ctx, post)
log.Debugf("Response received from mongo: %+v", res)
return err
}

func prepareNearbySortQuery(latitude, longitude float64) primitive.M {
query := bson.M{
"coordinates": bson.M{
"$near": bson.M{
"$geometry": bson.M{
"type": "Point",
"coordinates": []float64{longitude, latitude},
},
"$maxDistance": defaultNearbySearchRadius,
},
},
}
return query
}

func preparePaginatedNearbySortOpts(page int) *options.FindOptions {
findOptions := options.Find()
findOptions.SetLimit(int64(defaultPageLimit)) // Set the limit to the specified value
findOptions.SetSkip(int64((page - 1) * defaultPageLimit)) // Skip records based on the page number and limit
findOptions.SetSort(bson.D{{Key: "_id", Value: -1}}) // Sort by "_id" field in descending order
findOptions.SetProjection(bson.M{"coordinates": 0}) // Exclude "coordinates" field from the result
return findOptions
}

func (pr *PostsRepository) FindNearby(ctx context.Context, lat, long float64, page int) ([]models.Post, error) {
log.Debugf("Received new nearby req data: %f %f", lat, long)
query := prepareNearbySortQuery(lat, long)
optsWithPagination := preparePaginatedNearbySortOpts(page)

cur, err := pr.collection.Find(ctx, query, optsWithPagination)
if err != nil {
return nil, err
}
defer cur.Close(ctx)

var posts []models.Post
for cur.Next(ctx) {
var post models.Post
err := cur.Decode(&post)
if err != nil {
log.Errorf("Error while parsing doc: %s", err.Error())
continue
}
posts = append(posts, post)
}

if err := cur.Err(); err != nil {
return nil, err
}

return posts, nil
}

func (pr *PostsRepository) Like(context.Context, string) error {
return nil
}
54 changes: 54 additions & 0 deletions internal/post/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
package post

import (
"net/http"

"github.com/TheDevExperiment/server/internal/db/posts"
"github.com/TheDevExperiment/server/router/models"
"github.com/gin-gonic/gin"
)

func CreateV1(c *gin.Context) {
// first bind the req to our model
var req models.CreatePostRequest
var res models.CreatePostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

err := pr.Create(c, req.Post)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}
res.Message = "Created new post"
c.JSON(http.StatusOK, res)
}

func NearbyV1(c *gin.Context) {
// first bind the req to our model
var req models.NearbyPostRequest
var res models.NearbyPostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

posts, err := pr.FindNearby(c, req.Latitude, req.Longitude, req.Page)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}

res.Posts = posts
c.JSON(http.StatusOK, res)
}
57 changes: 21 additions & 36 deletions mongo-seed/posts.json
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,26 @@
[
{
"_id": {"$oid": "6170c708b210f8e6e31d6c94"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c91"},
"authorName": "John Doe",
"createdAt": {"$date": "2022-10-22T14:30:00.000Z"},
"content": {
"message": "hello world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "New York",
"countryId": "USA",
"coordinate": {
{
"_id": "6061f072c1760a272b4bb49a",
"userId": 12345,
"authorName": "John Doe",
"createdAt": "2023-04-10T10:30:00Z",
"cityId": 1,
"countryId": 2,
"coordinates": {
"type": "Point",
"coordinates": [-73.935242, 40.730610]
},
"isActive": true,
"likes": 5
"coordinates": [
-122.4194,
37.7749
]
},
{
"_id": {"$oid": "6170c708b210f8e6e31d6c95"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c92"},
"authorName": "Jane Smith",
"createdAt": {"$date": "2022-10-23T16:30:00.000Z"},
"content": {
"message": "bye world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "Toronto",
"countryId": "Canada",
"coordinate": {
"type": "Point",
"coordinates": [-79.3832, 43.6532]
},
"isActive": true,
"likes": 10
"isActive": true,
"likesCount": 10,
"commentsCount": 5,
"content": {
"text": "Lorem ipsum dolor sit amet",
"backgroundUrl": "https://example.com/image.jpg",
"backgroundType": 0
}
]
}
]

23 changes: 23 additions & 0 deletions router/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
package models

import "github.com/TheDevExperiment/server/internal/db/posts/models"

type CreatePostRequest struct {
Post models.Post `json:"post" binding:"required"`
}

type CreatePostResponse struct {
Message string `form:"message" json:"message" binding:"required"`
}

type NearbyPostRequest struct {
Latitude float64 `json:"latitude" binding:"required"`
Longitude float64 `json:"longitude" binding:"required"`
Page int ` json:"page"`
// Radius int -> if app wants to support tweakable filter
}

type NearbyPostResponse struct {
Posts []models.Post `json:"posts" binding:"required"`
Message string
}
3 changes: 3 additions & 0 deletions router/router.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@ package router
import (
"github.com/TheDevExperiment/server/internal/auth"
"github.com/TheDevExperiment/server/internal/db/repositories"
"github.com/TheDevExperiment/server/internal/post"
"github.com/gin-gonic/gin"
)

Expand All@@ -27,5 +28,7 @@ func SetupRouter() *gin.Engine {

// define all the routes
r.POST("/auth/v1/guest-validate", auth.GuestValidateV1)
r.POST("/post/v1/create", post.CreateV1)
r.POST("/post/v1/nearby", post.NearbyV1)
return r
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions internal/db/posts/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
package models

import "time"

// this struct will be used in internal packages
// for calling Post interface methods.
type Post struct {
Id string `bson:"_id,omitempty"`
UserId int `bson:"userId,omitempty"`
AuthorName string `bson:"authorName,omitempty"`
CreatedAt time.Time `bson:"createdAt,omitempty"`
CityId int `bson:"cityId,omitempty"`
CountryId int `bson:"countryId,omitempty"`
Coordinates Point `bson:"coordinates,omitempty"`
IsActive bool `bson:"isActive,omitempty"`
LikesCount int `bson:"likesCount,omitempty"`
CommentsCount int `bson:"commentsCount,omitempty"`
Content Content `bson:"content,omitempty,inline"`
}

type Point struct {
Type string `bson:"type"` //needs to be set to Point
Coordinates []float64 `bson:"coordinates"` //[long,lat]
}

type Content struct {
Text string `bson:"text,omitempty"`
BackgroundUrl string `bson:"backgroundUrl,omitempty"`
BackgroundType BackgroundType `bson:"backgroundType"`
}

type BackgroundType int

const (
PreloadedImage BackgroundType = iota
PreloadedVideo
CustomImage
CustomVideo
)
13 changes: 13 additions & 0 deletions internal/db/posts/posts.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db/posts/models"
)

type Posts interface {
Create(context.Context, models.Post) error
FindNearby(context.Context, float64, float64) ([]models.Post, error)
Like(context.Context, string) error
}
96 changes: 96 additions & 0 deletions internal/db/posts/posts_repository.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db"
"github.com/TheDevExperiment/server/internal/db/posts/models"
"github.com/TheDevExperiment/server/internal/log"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type PostsRepository struct {
collection *mongo.Collection
}

var singletonPostCollection *mongo.Collection

const defaultNearbySearchRadius = 1000 // 1km
const defaultPageLimit = 10

func NewPostsRepository() *PostsRepository {
// use mongodb to find collection here
if singletonPostCollection == nil {
log.Debug("Created connection to post collection")
singletonPostCollection = db.GetCollection(viper.GetString("mongodb.db_name"), "posts")
}
return &PostsRepository{collection: singletonPostCollection}
}

func (pr *PostsRepository) Create(ctx context.Context, post models.Post) error {
log.Debugf("Received new post create request data: %+v", post)
res, err := pr.collection.InsertOne(ctx, post)
log.Debugf("Response received from mongo: %+v", res)
return err
}

func prepareNearbySortQuery(latitude, longitude float64) primitive.M {
query := bson.M{
"coordinates": bson.M{
"$near": bson.M{
"$geometry": bson.M{
"type": "Point",
"coordinates": []float64{longitude, latitude},
},
"$maxDistance": defaultNearbySearchRadius,
},
},
}
return query
}

func preparePaginatedNearbySortOpts(page int) *options.FindOptions {
findOptions := options.Find()
findOptions.SetLimit(int64(defaultPageLimit)) // Set the limit to the specified value
findOptions.SetSkip(int64((page - 1) * defaultPageLimit)) // Skip records based on the page number and limit
findOptions.SetSort(bson.D{{Key: "_id", Value: -1}}) // Sort by "_id" field in descending order
findOptions.SetProjection(bson.M{"coordinates": 0}) // Exclude "coordinates" field from the result
return findOptions
}

func (pr *PostsRepository) FindNearby(ctx context.Context, lat, long float64, page int) ([]models.Post, error) {
log.Debugf("Received new nearby req data: %f %f", lat, long)
query := prepareNearbySortQuery(lat, long)
optsWithPagination := preparePaginatedNearbySortOpts(page)

cur, err := pr.collection.Find(ctx, query, optsWithPagination)
if err != nil {
return nil, err
}
defer cur.Close(ctx)

var posts []models.Post
for cur.Next(ctx) {
var post models.Post
err := cur.Decode(&post)
if err != nil {
log.Errorf("Error while parsing doc: %s", err.Error())
continue
}
posts = append(posts, post)
}

if err := cur.Err(); err != nil {
return nil, err
}

return posts, nil
}

func (pr *PostsRepository) Like(context.Context, string) error {
return nil
}
54 changes: 54 additions & 0 deletions internal/post/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
package post

import (
"net/http"

"github.com/TheDevExperiment/server/internal/db/posts"
"github.com/TheDevExperiment/server/router/models"
"github.com/gin-gonic/gin"
)

func CreateV1(c *gin.Context) {
// first bind the req to our model
var req models.CreatePostRequest
var res models.CreatePostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

err := pr.Create(c, req.Post)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}
res.Message = "Created new post"
c.JSON(http.StatusOK, res)
}

func NearbyV1(c *gin.Context) {
// first bind the req to our model
var req models.NearbyPostRequest
var res models.NearbyPostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

posts, err := pr.FindNearby(c, req.Latitude, req.Longitude, req.Page)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}

res.Posts = posts
c.JSON(http.StatusOK, res)
}
57 changes: 21 additions & 36 deletions mongo-seed/posts.json
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,26 @@
[
{
"_id": {"$oid": "6170c708b210f8e6e31d6c94"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c91"},
"authorName": "John Doe",
"createdAt": {"$date": "2022-10-22T14:30:00.000Z"},
"content": {
"message": "hello world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "New York",
"countryId": "USA",
"coordinate": {
{
"_id": "6061f072c1760a272b4bb49a",
"userId": 12345,
"authorName": "John Doe",
"createdAt": "2023-04-10T10:30:00Z",
"cityId": 1,
"countryId": 2,
"coordinates": {
"type": "Point",
"coordinates": [-73.935242, 40.730610]
},
"isActive": true,
"likes": 5
"coordinates": [
-122.4194,
37.7749
]
},
{
"_id": {"$oid": "6170c708b210f8e6e31d6c95"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c92"},
"authorName": "Jane Smith",
"createdAt": {"$date": "2022-10-23T16:30:00.000Z"},
"content": {
"message": "bye world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "Toronto",
"countryId": "Canada",
"coordinate": {
"type": "Point",
"coordinates": [-79.3832, 43.6532]
},
"isActive": true,
"likes": 10
"isActive": true,
"likesCount": 10,
"commentsCount": 5,
"content": {
"text": "Lorem ipsum dolor sit amet",
"backgroundUrl": "https://example.com/image.jpg",
"backgroundType": 0
}
]
}
]

23 changes: 23 additions & 0 deletions router/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
package models

import "github.com/TheDevExperiment/server/internal/db/posts/models"

type CreatePostRequest struct {
Post models.Post `json:"post" binding:"required"`
}

type CreatePostResponse struct {
Message string `form:"message" json:"message" binding:"required"`
}

type NearbyPostRequest struct {
Latitude float64 `json:"latitude" binding:"required"`
Longitude float64 `json:"longitude" binding:"required"`
Page int ` json:"page"`
// Radius int -> if app wants to support tweakable filter
}

type NearbyPostResponse struct {
Posts []models.Post `json:"posts" binding:"required"`
Message string
}
3 changes: 3 additions & 0 deletions router/router.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@ package router
import (
"github.com/TheDevExperiment/server/internal/auth"
"github.com/TheDevExperiment/server/internal/db/repositories"
"github.com/TheDevExperiment/server/internal/post"
"github.com/gin-gonic/gin"
)

Expand All@@ -27,5 +28,7 @@ func SetupRouter() *gin.Engine {

// define all the routes
r.POST("/auth/v1/guest-validate", auth.GuestValidateV1)
r.POST("/post/v1/create", post.CreateV1)
r.POST("/post/v1/nearby", post.NearbyV1)
return r
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions internal/db/posts/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
package models

import "time"

// this struct will be used in internal packages
// for calling Post interface methods.
type Post struct {
Id string `bson:"_id,omitempty"`
UserId int `bson:"userId,omitempty"`
AuthorName string `bson:"authorName,omitempty"`
CreatedAt time.Time `bson:"createdAt,omitempty"`
CityId int `bson:"cityId,omitempty"`
CountryId int `bson:"countryId,omitempty"`
Coordinates Point `bson:"coordinates,omitempty"`
IsActive bool `bson:"isActive,omitempty"`
LikesCount int `bson:"likesCount,omitempty"`
CommentsCount int `bson:"commentsCount,omitempty"`
Content Content `bson:"content,omitempty,inline"`
}

type Point struct {
Type string `bson:"type"` //needs to be set to Point
Coordinates []float64 `bson:"coordinates"` //[long,lat]
}

type Content struct {
Text string `bson:"text,omitempty"`
BackgroundUrl string `bson:"backgroundUrl,omitempty"`
BackgroundType BackgroundType `bson:"backgroundType"`
}

type BackgroundType int

const (
PreloadedImage BackgroundType = iota
PreloadedVideo
CustomImage
CustomVideo
)
13 changes: 13 additions & 0 deletions internal/db/posts/posts.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db/posts/models"
)

type Posts interface {
Create(context.Context, models.Post) error
FindNearby(context.Context, float64, float64) ([]models.Post, error)
Like(context.Context, string) error
}
96 changes: 96 additions & 0 deletions internal/db/posts/posts_repository.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db"
"github.com/TheDevExperiment/server/internal/db/posts/models"
"github.com/TheDevExperiment/server/internal/log"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type PostsRepository struct {
collection *mongo.Collection
}

var singletonPostCollection *mongo.Collection

const defaultNearbySearchRadius = 1000 // 1km
const defaultPageLimit = 10

func NewPostsRepository() *PostsRepository {
// use mongodb to find collection here
if singletonPostCollection == nil {
log.Debug("Created connection to post collection")
singletonPostCollection = db.GetCollection(viper.GetString("mongodb.db_name"), "posts")
}
return &PostsRepository{collection: singletonPostCollection}
}

func (pr *PostsRepository) Create(ctx context.Context, post models.Post) error {
log.Debugf("Received new post create request data: %+v", post)
res, err := pr.collection.InsertOne(ctx, post)
log.Debugf("Response received from mongo: %+v", res)
return err
}

func prepareNearbySortQuery(latitude, longitude float64) primitive.M {
query := bson.M{
"coordinates": bson.M{
"$near": bson.M{
"$geometry": bson.M{
"type": "Point",
"coordinates": []float64{longitude, latitude},
},
"$maxDistance": defaultNearbySearchRadius,
},
},
}
return query
}

func preparePaginatedNearbySortOpts(page int) *options.FindOptions {
findOptions := options.Find()
findOptions.SetLimit(int64(defaultPageLimit)) // Set the limit to the specified value
findOptions.SetSkip(int64((page - 1) * defaultPageLimit)) // Skip records based on the page number and limit
findOptions.SetSort(bson.D{{Key: "_id", Value: -1}}) // Sort by "_id" field in descending order
findOptions.SetProjection(bson.M{"coordinates": 0}) // Exclude "coordinates" field from the result
return findOptions
}

func (pr *PostsRepository) FindNearby(ctx context.Context, lat, long float64, page int) ([]models.Post, error) {
log.Debugf("Received new nearby req data: %f %f", lat, long)
query := prepareNearbySortQuery(lat, long)
optsWithPagination := preparePaginatedNearbySortOpts(page)

cur, err := pr.collection.Find(ctx, query, optsWithPagination)
if err != nil {
return nil, err
}
defer cur.Close(ctx)

var posts []models.Post
for cur.Next(ctx) {
var post models.Post
err := cur.Decode(&post)
if err != nil {
log.Errorf("Error while parsing doc: %s", err.Error())
continue
}
posts = append(posts, post)
}

if err := cur.Err(); err != nil {
return nil, err
}

return posts, nil
}

func (pr *PostsRepository) Like(context.Context, string) error {
return nil
}
54 changes: 54 additions & 0 deletions internal/post/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
package post

import (
"net/http"

"github.com/TheDevExperiment/server/internal/db/posts"
"github.com/TheDevExperiment/server/router/models"
"github.com/gin-gonic/gin"
)

func CreateV1(c *gin.Context) {
// first bind the req to our model
var req models.CreatePostRequest
var res models.CreatePostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

err := pr.Create(c, req.Post)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}
res.Message = "Created new post"
c.JSON(http.StatusOK, res)
}

func NearbyV1(c *gin.Context) {
// first bind the req to our model
var req models.NearbyPostRequest
var res models.NearbyPostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

posts, err := pr.FindNearby(c, req.Latitude, req.Longitude, req.Page)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}

res.Posts = posts
c.JSON(http.StatusOK, res)
}
57 changes: 21 additions & 36 deletions mongo-seed/posts.json
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,26 @@
[
{
"_id": {"$oid": "6170c708b210f8e6e31d6c94"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c91"},
"authorName": "John Doe",
"createdAt": {"$date": "2022-10-22T14:30:00.000Z"},
"content": {
"message": "hello world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "New York",
"countryId": "USA",
"coordinate": {
{
"_id": "6061f072c1760a272b4bb49a",
"userId": 12345,
"authorName": "John Doe",
"createdAt": "2023-04-10T10:30:00Z",
"cityId": 1,
"countryId": 2,
"coordinates": {
"type": "Point",
"coordinates": [-73.935242, 40.730610]
},
"isActive": true,
"likes": 5
"coordinates": [
-122.4194,
37.7749
]
},
{
"_id": {"$oid": "6170c708b210f8e6e31d6c95"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c92"},
"authorName": "Jane Smith",
"createdAt": {"$date": "2022-10-23T16:30:00.000Z"},
"content": {
"message": "bye world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "Toronto",
"countryId": "Canada",
"coordinate": {
"type": "Point",
"coordinates": [-79.3832, 43.6532]
},
"isActive": true,
"likes": 10
"isActive": true,
"likesCount": 10,
"commentsCount": 5,
"content": {
"text": "Lorem ipsum dolor sit amet",
"backgroundUrl": "https://example.com/image.jpg",
"backgroundType": 0
}
]
}
]

23 changes: 23 additions & 0 deletions router/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
package models

import "github.com/TheDevExperiment/server/internal/db/posts/models"

type CreatePostRequest struct {
Post models.Post `json:"post" binding:"required"`
}

type CreatePostResponse struct {
Message string `form:"message" json:"message" binding:"required"`
}

type NearbyPostRequest struct {
Latitude float64 `json:"latitude" binding:"required"`
Longitude float64 `json:"longitude" binding:"required"`
Page int ` json:"page"`
// Radius int -> if app wants to support tweakable filter
}

type NearbyPostResponse struct {
Posts []models.Post `json:"posts" binding:"required"`
Message string
}
3 changes: 3 additions & 0 deletions router/router.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@ package router
import (
"github.com/TheDevExperiment/server/internal/auth"
"github.com/TheDevExperiment/server/internal/db/repositories"
"github.com/TheDevExperiment/server/internal/post"
"github.com/gin-gonic/gin"
)

Expand All@@ -27,5 +28,7 @@ func SetupRouter() *gin.Engine {

// define all the routes
r.POST("/auth/v1/guest-validate", auth.GuestValidateV1)
r.POST("/post/v1/create", post.CreateV1)
r.POST("/post/v1/nearby", post.NearbyV1)
return r
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions internal/db/posts/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
package models

import "time"

// this struct will be used in internal packages
// for calling Post interface methods.
type Post struct {
Id string `bson:"_id,omitempty"`
UserId int `bson:"userId,omitempty"`
AuthorName string `bson:"authorName,omitempty"`
CreatedAt time.Time `bson:"createdAt,omitempty"`
CityId int `bson:"cityId,omitempty"`
CountryId int `bson:"countryId,omitempty"`
Coordinates Point `bson:"coordinates,omitempty"`
IsActive bool `bson:"isActive,omitempty"`
LikesCount int `bson:"likesCount,omitempty"`
CommentsCount int `bson:"commentsCount,omitempty"`
Content Content `bson:"content,omitempty,inline"`
}

type Point struct {
Type string `bson:"type"` //needs to be set to Point
Coordinates []float64 `bson:"coordinates"` //[long,lat]
}

type Content struct {
Text string `bson:"text,omitempty"`
BackgroundUrl string `bson:"backgroundUrl,omitempty"`
BackgroundType BackgroundType `bson:"backgroundType"`
}

type BackgroundType int

const (
PreloadedImage BackgroundType = iota
PreloadedVideo
CustomImage
CustomVideo
)
13 changes: 13 additions & 0 deletions internal/db/posts/posts.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db/posts/models"
)

type Posts interface {
Create(context.Context, models.Post) error
FindNearby(context.Context, float64, float64) ([]models.Post, error)
Like(context.Context, string) error
}
96 changes: 96 additions & 0 deletions internal/db/posts/posts_repository.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
package posts

import (
"context"

"github.com/TheDevExperiment/server/internal/db"
"github.com/TheDevExperiment/server/internal/db/posts/models"
"github.com/TheDevExperiment/server/internal/log"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type PostsRepository struct {
collection *mongo.Collection
}

var singletonPostCollection *mongo.Collection

const defaultNearbySearchRadius = 1000 // 1km
const defaultPageLimit = 10

func NewPostsRepository() *PostsRepository {
// use mongodb to find collection here
if singletonPostCollection == nil {
log.Debug("Created connection to post collection")
singletonPostCollection = db.GetCollection(viper.GetString("mongodb.db_name"), "posts")
}
return &PostsRepository{collection: singletonPostCollection}
}

func (pr *PostsRepository) Create(ctx context.Context, post models.Post) error {
log.Debugf("Received new post create request data: %+v", post)
res, err := pr.collection.InsertOne(ctx, post)
log.Debugf("Response received from mongo: %+v", res)
return err
}

func prepareNearbySortQuery(latitude, longitude float64) primitive.M {
query := bson.M{
"coordinates": bson.M{
"$near": bson.M{
"$geometry": bson.M{
"type": "Point",
"coordinates": []float64{longitude, latitude},
},
"$maxDistance": defaultNearbySearchRadius,
},
},
}
return query
}

func preparePaginatedNearbySortOpts(page int) *options.FindOptions {
findOptions := options.Find()
findOptions.SetLimit(int64(defaultPageLimit)) // Set the limit to the specified value
findOptions.SetSkip(int64((page - 1) * defaultPageLimit)) // Skip records based on the page number and limit
findOptions.SetSort(bson.D{{Key: "_id", Value: -1}}) // Sort by "_id" field in descending order
findOptions.SetProjection(bson.M{"coordinates": 0}) // Exclude "coordinates" field from the result
return findOptions
}

func (pr *PostsRepository) FindNearby(ctx context.Context, lat, long float64, page int) ([]models.Post, error) {
log.Debugf("Received new nearby req data: %f %f", lat, long)
query := prepareNearbySortQuery(lat, long)
optsWithPagination := preparePaginatedNearbySortOpts(page)

cur, err := pr.collection.Find(ctx, query, optsWithPagination)
if err != nil {
return nil, err
}
defer cur.Close(ctx)

var posts []models.Post
for cur.Next(ctx) {
var post models.Post
err := cur.Decode(&post)
if err != nil {
log.Errorf("Error while parsing doc: %s", err.Error())
continue
}
posts = append(posts, post)
}

if err := cur.Err(); err != nil {
return nil, err
}

return posts, nil
}

func (pr *PostsRepository) Like(context.Context, string) error {
return nil
}
54 changes: 54 additions & 0 deletions internal/post/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
package post

import (
"net/http"

"github.com/TheDevExperiment/server/internal/db/posts"
"github.com/TheDevExperiment/server/router/models"
"github.com/gin-gonic/gin"
)

func CreateV1(c *gin.Context) {
// first bind the req to our model
var req models.CreatePostRequest
var res models.CreatePostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

err := pr.Create(c, req.Post)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}
res.Message = "Created new post"
c.JSON(http.StatusOK, res)
}

func NearbyV1(c *gin.Context) {
// first bind the req to our model
var req models.NearbyPostRequest
var res models.NearbyPostResponse
pr := posts.NewPostsRepository()

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

posts, err := pr.FindNearby(c, req.Latitude, req.Longitude, req.Page)
if err != nil {
httpCode := http.StatusInternalServerError
res.Message = err.Error()
c.JSON(httpCode, res)
return
}

res.Posts = posts
c.JSON(http.StatusOK, res)
}
57 changes: 21 additions & 36 deletions mongo-seed/posts.json
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,26 @@
[
{
"_id": {"$oid": "6170c708b210f8e6e31d6c94"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c91"},
"authorName": "John Doe",
"createdAt": {"$date": "2022-10-22T14:30:00.000Z"},
"content": {
"message": "hello world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "New York",
"countryId": "USA",
"coordinate": {
{
"_id": "6061f072c1760a272b4bb49a",
"userId": 12345,
"authorName": "John Doe",
"createdAt": "2023-04-10T10:30:00Z",
"cityId": 1,
"countryId": 2,
"coordinates": {
"type": "Point",
"coordinates": [-73.935242, 40.730610]
},
"isActive": true,
"likes": 5
"coordinates": [
-122.4194,
37.7749
]
},
{
"_id": {"$oid": "6170c708b210f8e6e31d6c95"},
"userId": {"$oid": "6170c6d9b210f8e6e31d6c92"},
"authorName": "Jane Smith",
"createdAt": {"$date": "2022-10-23T16:30:00.000Z"},
"content": {
"message": "bye world",
"fetchUrl": null,
"backgroundType": "Image"
},
"cityId": "Toronto",
"countryId": "Canada",
"coordinate": {
"type": "Point",
"coordinates": [-79.3832, 43.6532]
},
"isActive": true,
"likes": 10
"isActive": true,
"likesCount": 10,
"commentsCount": 5,
"content": {
"text": "Lorem ipsum dolor sit amet",
"backgroundUrl": "https://example.com/image.jpg",
"backgroundType": 0
}
]
}
]

23 changes: 23 additions & 0 deletions router/models/post.go
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
package models

import "github.com/TheDevExperiment/server/internal/db/posts/models"

type CreatePostRequest struct {
Post models.Post `json:"post" binding:"required"`
}

type CreatePostResponse struct {
Message string `form:"message" json:"message" binding:"required"`
}

type NearbyPostRequest struct {
Latitude float64 `json:"latitude" binding:"required"`
Longitude float64 `json:"longitude" binding:"required"`
Page int ` json:"page"`
// Radius int -> if app wants to support tweakable filter
}

type NearbyPostResponse struct {
Posts []models.Post `json:"posts" binding:"required"`
Message string
}
3 changes: 3 additions & 0 deletions router/router.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@ package router
import (
"github.com/TheDevExperiment/server/internal/auth"
"github.com/TheDevExperiment/server/internal/db/repositories"
"github.com/TheDevExperiment/server/internal/post"
"github.com/gin-gonic/gin"
)

Expand All@@ -27,5 +28,7 @@ func SetupRouter() *gin.Engine {

// define all the routes
r.POST("/auth/v1/guest-validate", auth.GuestValidateV1)
r.POST("/post/v1/create", post.CreateV1)
r.POST("/post/v1/nearby", post.NearbyV1)
return r
}