Add go links and minor refactoring
This commit is contained in:
parent
22c2b9f665
commit
3142d0a4be
@ -8,7 +8,6 @@ COPY lib/ /app
|
|||||||
|
|
||||||
RUN rm -f /app/.env
|
RUN rm -f /app/.env
|
||||||
|
|
||||||
COPY .env.docker /app/.env
|
|
||||||
COPY package.json /app
|
COPY package.json /app
|
||||||
COPY pnpm-lock.yaml /app
|
COPY pnpm-lock.yaml /app
|
||||||
|
|
||||||
|
12
ex
12
ex
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash -e
|
#!/bin/bash
|
||||||
|
|
||||||
ENV_NODE="$(which node)"
|
ENV_NODE="$(which node)"
|
||||||
ENV_PNPM="$(which pnpm)"
|
ENV_PNPM="$(which pnpm)"
|
||||||
@ -134,7 +134,13 @@ if [ ! -d "./node_modules" ]; then
|
|||||||
printf "\033[32m✓\033[39m Looks like you're all set up! Run this command again to access the Extollo CLI.\n"
|
printf "\033[32m✓\033[39m Looks like you're all set up! Run this command again to access the Extollo CLI.\n"
|
||||||
else
|
else
|
||||||
start_spinner "Building your app..."
|
start_spinner "Building your app..."
|
||||||
"$ENV_PNPM" run build > /dev/null
|
BUILD_OUTPUT="$($ENV_PNPM run build 2>&1)"
|
||||||
stop_spinner 0
|
BUILD_EC=$?
|
||||||
|
stop_spinner $BUILD_EC
|
||||||
|
if [ $BUILD_EC -ne 0 ]; then
|
||||||
|
printf "\033[31m✘\033[39m Uh, oh! Looks like your application failed to build. (exit: $BUILD_EC)"
|
||||||
|
echo "$BUILD_OUTPUT"
|
||||||
|
exit $BUILD_EC
|
||||||
|
fi
|
||||||
"$ENV_NODE" --experimental-repl-await ./lib/cli.js $@
|
"$ENV_NODE" --experimental-repl-await ./lib/cli.js $@
|
||||||
fi
|
fi
|
||||||
|
@ -8,8 +8,9 @@
|
|||||||
"lib": "lib"
|
"lib": "lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@extollo/lib": "^0.9.21",
|
"@extollo/lib": "^0.9.28",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
|
"feed": "^4.2.2",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"ts-expose-internals": "^4.5.4",
|
"ts-expose-internals": "^4.5.4",
|
||||||
"ts-patch": "^2.0.1",
|
"ts-patch": "^2.0.1",
|
||||||
|
@ -2,8 +2,9 @@ lockfileVersion: 5.3
|
|||||||
|
|
||||||
specifiers:
|
specifiers:
|
||||||
'@extollo/cc': ^0.6.0
|
'@extollo/cc': ^0.6.0
|
||||||
'@extollo/lib': ^0.9.21
|
'@extollo/lib': ^0.9.28
|
||||||
copyfiles: ^2.4.1
|
copyfiles: ^2.4.1
|
||||||
|
feed: ^4.2.2
|
||||||
rimraf: ^3.0.2
|
rimraf: ^3.0.2
|
||||||
ts-expose-internals: ^4.5.4
|
ts-expose-internals: ^4.5.4
|
||||||
ts-patch: ^2.0.1
|
ts-patch: ^2.0.1
|
||||||
@ -12,8 +13,9 @@ specifiers:
|
|||||||
zod: ^3.11.6
|
zod: ^3.11.6
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@extollo/lib': 0.9.21
|
'@extollo/lib': 0.9.28
|
||||||
copyfiles: 2.4.1
|
copyfiles: 2.4.1
|
||||||
|
feed: 4.2.2
|
||||||
rimraf: 3.0.2
|
rimraf: 3.0.2
|
||||||
ts-expose-internals: 4.5.4
|
ts-expose-internals: 4.5.4
|
||||||
ts-patch: 2.0.1_typescript@4.3.2
|
ts-patch: 2.0.1_typescript@4.3.2
|
||||||
@ -110,8 +112,8 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@extollo/lib/0.9.21:
|
/@extollo/lib/0.9.28:
|
||||||
resolution: {integrity: sha512-JKgUQWS9/lEho9ardayvZO8pkE4ZoPs4xuo6KAQDqp7wq4PjGjrGBKeHX7ViHeaFm/XelskaG9eu9Ox9RvirFQ==}
|
resolution: {integrity: sha512-lMI2FWOTKbRsqJrOAvZofzfz0DaNwWecYsDX98XkNbktaUjhaGc7xr1OVxscxUC/Edapyj/p02MLdsUoIsARxA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@atao60/fse-cli': 0.1.7
|
'@atao60/fse-cli': 0.1.7
|
||||||
'@extollo/ui': 0.1.0_@types+node@14.18.12
|
'@extollo/ui': 0.1.0_@types+node@14.18.12
|
||||||
@ -987,6 +989,13 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
reusify: 1.0.4
|
reusify: 1.0.4
|
||||||
|
|
||||||
|
/feed/4.2.2:
|
||||||
|
resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==}
|
||||||
|
engines: {node: '>=0.4.0'}
|
||||||
|
dependencies:
|
||||||
|
xml-js: 1.6.11
|
||||||
|
dev: false
|
||||||
|
|
||||||
/fetch-blob/3.1.5:
|
/fetch-blob/3.1.5:
|
||||||
resolution: {integrity: sha512-N64ZpKqoLejlrwkIAnb9iLSA3Vx/kjgzpcDhygcqJ2KKjky8nCgUQ+dzXtbrLaWZGZNmNfQTsiQ0weZ1svglHg==}
|
resolution: {integrity: sha512-N64ZpKqoLejlrwkIAnb9iLSA3Vx/kjgzpcDhygcqJ2KKjky8nCgUQ+dzXtbrLaWZGZNmNfQTsiQ0weZ1svglHg==}
|
||||||
engines: {node: ^12.20 || >= 14.13}
|
engines: {node: ^12.20 || >= 14.13}
|
||||||
@ -2001,6 +2010,10 @@ packages:
|
|||||||
/safer-buffer/2.1.2:
|
/safer-buffer/2.1.2:
|
||||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||||
|
|
||||||
|
/sax/1.2.4:
|
||||||
|
resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/semver/5.7.1:
|
/semver/5.7.1:
|
||||||
resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
|
resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@ -2577,6 +2590,13 @@ packages:
|
|||||||
/wrappy/1.0.2:
|
/wrappy/1.0.2:
|
||||||
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
|
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
|
||||||
|
|
||||||
|
/xml-js/1.6.11:
|
||||||
|
resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
sax: 1.2.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
/xtend/4.0.2:
|
/xtend/4.0.2:
|
||||||
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
||||||
engines: {node: '>=0.4'}
|
engines: {node: '>=0.4'}
|
||||||
|
8
src/app/configs/redis.config.ts
Normal file
8
src/app/configs/redis.config.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import {env, RedisOptions} from '@extollo/lib'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
connection: {
|
||||||
|
port: env('REDIS_PORT', 6379),
|
||||||
|
host: env('REDIS_HOST', '127.0.0.1'),
|
||||||
|
} as RedisOptions
|
||||||
|
}
|
16
src/app/http/controllers/Feed.controller.ts
Normal file
16
src/app/http/controllers/Feed.controller.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {Controller, view, Injectable, Collection} from '@extollo/lib'
|
||||||
|
import {FeedPost} from '../../models/FeedPost.model'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feed Controller
|
||||||
|
* ------------------------------------
|
||||||
|
* Backend for routes related to my post feed.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class Feed extends Controller {
|
||||||
|
public async feed(feedPosts: Collection<FeedPost>) {
|
||||||
|
return view('feed', {
|
||||||
|
feedPosts: feedPosts.toArray(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
22
src/app/http/controllers/GoLinks.controller.ts
Normal file
22
src/app/http/controllers/GoLinks.controller.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import {Controller, view, Inject, Injectable, HTTPStatus, http, redirect} from '@extollo/lib'
|
||||||
|
import {GoLink} from '../../models/GoLink.model'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GoLinks Controller
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class GoLinks extends Controller {
|
||||||
|
async launch() {
|
||||||
|
const short = this.request.safe('short').string()
|
||||||
|
const link = await GoLink.query<GoLink>()
|
||||||
|
.where('active', '=', true)
|
||||||
|
.where('short', '=', short)
|
||||||
|
.first()
|
||||||
|
|
||||||
|
if ( !link ) {
|
||||||
|
return http(HTTPStatus.http404, 'Invalid or expired link.')
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect(link.url)
|
||||||
|
}
|
||||||
|
}
|
@ -13,9 +13,8 @@ export class Home extends Controller {
|
|||||||
@Inject()
|
@Inject()
|
||||||
protected readonly routing!: Routing
|
protected readonly routing!: Routing
|
||||||
|
|
||||||
public async welcome() {
|
public async welcome(feedPosts: Collection<FeedPost>) {
|
||||||
const workItems = await this.getWorkItems()
|
const workItems = await this.getWorkItems()
|
||||||
const feedPosts = await this.getFeedPosts()
|
|
||||||
|
|
||||||
return view('welcome', {
|
return view('welcome', {
|
||||||
feedPosts: feedPosts.toArray(),
|
feedPosts: feedPosts.toArray(),
|
||||||
@ -30,13 +29,6 @@ export class Home extends Controller {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async feed() {
|
|
||||||
const feedPosts = await this.getFeedPosts(true)
|
|
||||||
return view('feed', {
|
|
||||||
feedPosts: feedPosts.toArray(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
public technical() {
|
public technical() {
|
||||||
const isOptOut = this.request.cookies.has(this.config.get('app.analytics.optOutCookie'))
|
const isOptOut = this.request.cookies.has(this.config.get('app.analytics.optOutCookie'))
|
||||||
|
|
||||||
@ -73,21 +65,6 @@ export class Home extends Controller {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async getFeedPosts(all = false): Promise<Collection<FeedPost>> {
|
|
||||||
const query = FeedPost.query<FeedPost>()
|
|
||||||
.orderByDescending('posted_at')
|
|
||||||
|
|
||||||
if ( !all ) {
|
|
||||||
query.limit(6)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !this.security.hasUser() ) {
|
|
||||||
query.where('visible', '=', true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return query.get().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async getWorkItems(): Promise<Collection<WorkItem>> {
|
protected async getWorkItems(): Promise<Collection<WorkItem>> {
|
||||||
const query = WorkItem.query<WorkItem>()
|
const query = WorkItem.query<WorkItem>()
|
||||||
.orderByDescending('start_date')
|
.orderByDescending('start_date')
|
||||||
|
@ -4,23 +4,14 @@ import {Snippet} from '../../models/Snippet.model'
|
|||||||
/**
|
/**
|
||||||
* Snippets Controller
|
* Snippets Controller
|
||||||
* ------------------------------------
|
* ------------------------------------
|
||||||
* Put some description here.
|
* Backend for routes that deal with code snippets.
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Snippets extends Controller {
|
export class Snippets extends Controller {
|
||||||
@Inject()
|
@Inject()
|
||||||
protected readonly logging!: Logging
|
protected readonly logging!: Logging
|
||||||
|
|
||||||
public async viewSnippet() {
|
public async viewSnippet(snippet: Snippet) {
|
||||||
const slug = this.request.safe('slug').string()
|
|
||||||
const snippet = await Snippet.query<Snippet>()
|
|
||||||
.where('slug', '=', slug)
|
|
||||||
.first()
|
|
||||||
|
|
||||||
if ( !snippet ) {
|
|
||||||
return http(HTTPStatus.http404, 'Snippet URL is invalid.')
|
|
||||||
}
|
|
||||||
|
|
||||||
const needsAccessKey = snippet?.accessKey && snippet.accessKey !== this.request.input('accessKey')
|
const needsAccessKey = snippet?.accessKey && snippet.accessKey !== this.request.input('accessKey')
|
||||||
const needsConfirm = snippet?.singleAccessOnly && !this.request.input('confirmSingleAccess')
|
const needsConfirm = snippet?.singleAccessOnly && !this.request.input('confirmSingleAccess')
|
||||||
|
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
import {Injectable, ParameterMiddleware, Collection, SecurityContext, Either, ResponseObject, right, Inject} from '@extollo/lib'
|
||||||
|
import {FeedPost} from '../../../models/FeedPost.model'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LoadFeedPosts Middleware
|
||||||
|
* --------------------------------------------
|
||||||
|
* Load feed posts visible to the user as a handler parameter.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class LoadFeedPosts extends ParameterMiddleware<Collection<FeedPost>, [] | [{ all: boolean }]> {
|
||||||
|
@Inject()
|
||||||
|
protected readonly security!: SecurityContext
|
||||||
|
|
||||||
|
async handle({ all = false } = {}): Promise<Either<ResponseObject, Collection<FeedPost>>> {
|
||||||
|
const query = FeedPost.query<FeedPost>()
|
||||||
|
.orderByDescending('posted_at')
|
||||||
|
|
||||||
|
if ( !all ) {
|
||||||
|
query.limit(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !this.security.hasUser() ) {
|
||||||
|
query.where('visible', '=', true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return right(await query.get().collect())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
import {
|
||||||
|
Either,
|
||||||
|
http,
|
||||||
|
HTTPStatus,
|
||||||
|
Inject,
|
||||||
|
Injectable,
|
||||||
|
left,
|
||||||
|
ParameterMiddleware,
|
||||||
|
ResponseObject, right,
|
||||||
|
SecurityContext,
|
||||||
|
} from '@extollo/lib'
|
||||||
|
import {Snippet} from '../../../models/Snippet.model'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LoadSnippet Middleware
|
||||||
|
* --------------------------------------------
|
||||||
|
* Look up the Snippet instance from the request parameters.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class LoadSnippet extends ParameterMiddleware<Snippet> {
|
||||||
|
@Inject()
|
||||||
|
protected readonly security!: SecurityContext
|
||||||
|
|
||||||
|
async handle(): Promise<Either<ResponseObject, Snippet>> {
|
||||||
|
const slug = String(this.request.input('slug') || '')
|
||||||
|
if ( !slug ) {
|
||||||
|
return left(http(HTTPStatus.http404))
|
||||||
|
}
|
||||||
|
|
||||||
|
const snippet = await Snippet.query<Snippet>()
|
||||||
|
.where('slug', '=', slug)
|
||||||
|
.first()
|
||||||
|
|
||||||
|
if ( !snippet || snippet.usersOnly && !this.security.hasUser() ) {
|
||||||
|
return left(http(HTTPStatus.http404))
|
||||||
|
}
|
||||||
|
|
||||||
|
return right(snippet)
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,15 @@ import {Route, SessionAuthMiddleware} from '@extollo/lib'
|
|||||||
import {Home} from '../controllers/Home.controller'
|
import {Home} from '../controllers/Home.controller'
|
||||||
import {PageView} from '../middlewares/PageView.middleware'
|
import {PageView} from '../middlewares/PageView.middleware'
|
||||||
import {Snippets} from '../controllers/Snippets.controller'
|
import {Snippets} from '../controllers/Snippets.controller'
|
||||||
|
import {GoLinks} from '../controllers/GoLinks.controller'
|
||||||
|
import {Feed} from '../controllers/Feed.controller'
|
||||||
|
import {LoadSnippet} from '../middlewares/parameters/LoadSnippet.middleware'
|
||||||
|
import {LoadFeedPosts} from '../middlewares/parameters/LoadFeedPosts.middleware'
|
||||||
|
|
||||||
Route
|
Route
|
||||||
.group('/', () => {
|
.group('/', () => {
|
||||||
Route.get('/')
|
Route.get('/')
|
||||||
|
.parameterMiddleware(LoadFeedPosts)
|
||||||
.calls<Home>(Home, home => home.welcome)
|
.calls<Home>(Home, home => home.welcome)
|
||||||
.alias('home')
|
.alias('home')
|
||||||
|
|
||||||
@ -21,16 +26,16 @@ Route
|
|||||||
.alias('opt-out')
|
.alias('opt-out')
|
||||||
|
|
||||||
Route.get('/feed')
|
Route.get('/feed')
|
||||||
.calls<Home>(Home, home => home.feed)
|
.parameterMiddleware(LoadFeedPosts, {all: true})
|
||||||
|
.calls<Feed>(Feed, feed => feed.feed)
|
||||||
.alias('feed')
|
.alias('feed')
|
||||||
|
|
||||||
Route.get('/snippet/:slug')
|
Route.get('/snippet/:slug')
|
||||||
|
.parameterMiddleware(LoadSnippet)
|
||||||
.calls<Snippets>(Snippets, snippets => snippets.viewSnippet)
|
.calls<Snippets>(Snippets, snippets => snippets.viewSnippet)
|
||||||
|
|
||||||
Route.get('/test')
|
Route.any('/go/:short')
|
||||||
.handledBy(() => {
|
.calls<GoLinks>(GoLinks, go => go.launch)
|
||||||
return 'Hello, World!'
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.pre(SessionAuthMiddleware)
|
.pre(SessionAuthMiddleware)
|
||||||
.pre(PageView)
|
.pre(PageView)
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
import {DatabaseService, FieldType, Inject, Injectable, Migration} from '@extollo/lib'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateGolinksTableMigration
|
||||||
|
* ----------------------------------
|
||||||
|
* Create Table to store URL redirections
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export default class CreateGolinksTableMigration extends Migration {
|
||||||
|
@Inject()
|
||||||
|
protected readonly db!: DatabaseService
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the migration.
|
||||||
|
*/
|
||||||
|
async up(): Promise<void> {
|
||||||
|
const schema = this.db.get().schema()
|
||||||
|
const table = await schema.table('go_links')
|
||||||
|
|
||||||
|
table.primaryKey('go_link_id').required()
|
||||||
|
|
||||||
|
table.column('active')
|
||||||
|
.type(FieldType.bool)
|
||||||
|
.default(true)
|
||||||
|
|
||||||
|
table.column('short')
|
||||||
|
.type(FieldType.varchar)
|
||||||
|
.required()
|
||||||
|
.unique()
|
||||||
|
|
||||||
|
table.column('url')
|
||||||
|
.type(FieldType.text)
|
||||||
|
.required()
|
||||||
|
|
||||||
|
await schema.commit(table)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo the migration.
|
||||||
|
*/
|
||||||
|
async down(): Promise<void> {
|
||||||
|
const schema = this.db.get().schema()
|
||||||
|
const table = await schema.table('go_links')
|
||||||
|
|
||||||
|
table.dropIfExists()
|
||||||
|
|
||||||
|
await schema.commit(table)
|
||||||
|
}
|
||||||
|
}
|
24
src/app/models/GoLink.model.ts
Normal file
24
src/app/models/GoLink.model.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import {Field, FieldType, Injectable, Model} from '@extollo/lib'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GoLink Model
|
||||||
|
* -----------------------------------
|
||||||
|
* A shortened URL.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class GoLink extends Model<GoLink> {
|
||||||
|
protected static table = 'go_links'
|
||||||
|
protected static key = 'go_link_id'
|
||||||
|
|
||||||
|
@Field(FieldType.serial, 'go_link_id')
|
||||||
|
protected id!: number
|
||||||
|
|
||||||
|
@Field(FieldType.bool)
|
||||||
|
public active = true
|
||||||
|
|
||||||
|
@Field(FieldType.varchar)
|
||||||
|
public short!: string
|
||||||
|
|
||||||
|
@Field(FieldType.text)
|
||||||
|
public url!: string
|
||||||
|
}
|
@ -64,19 +64,6 @@ block content
|
|||||||
p.text !{item.body}
|
p.text !{item.body}
|
||||||
.bottom
|
.bottom
|
||||||
.stamp <a href="#{named('feed')}##{item.feedPostId}" class="feed-edit-button">permalink</a> | #{item.postedAt.toLocaleString()}
|
.stamp <a href="#{named('feed')}##{item.feedPostId}" class="feed-edit-button">permalink</a> | #{item.postedAt.toLocaleString()}
|
||||||
// each item in feed_items
|
.row.mt-4
|
||||||
// .feed-item
|
.col-12.text-center
|
||||||
// div.feed-category(id='feedPostTag_' + item.id)
|
a.button(href="/feed") view all
|
||||||
// .tag #{item.tag}
|
|
||||||
// span
|
|
||||||
// if item.draft
|
|
||||||
// p.text (draft)
|
|
||||||
// p.text !{item.text}
|
|
||||||
// .bottom
|
|
||||||
// if user && can_access.feed_delete && can_access.feed_edit
|
|
||||||
// .stamp <a href="/dash/c/form/FeedPost?id=#{item.id}" class="feed-edit-button" target="_blank">edit</a> | <a href="#recent" class="feed-delete-button" postid="#{item.id}">delete</a>
|
|
||||||
// .stamp <a href="#{app.url}feed##{item.id}" class="feed-edit-button">permalink</a> | #{item.date.toLocaleString()}
|
|
||||||
if feed_overflow
|
|
||||||
.row.mt-4
|
|
||||||
.col-12.text-center
|
|
||||||
a.button(href="/feed") view more
|
|
||||||
|
Loading…
Reference in New Issue
Block a user