mirror of
https://github.com/Troplo/Colubrina.git
synced 2024-12-26 00:15:15 +11:00
1.0.19
This commit is contained in:
parent
b292277ec4
commit
0d6e209fc6
22 changed files with 424 additions and 207 deletions
|
@ -1,15 +1,3 @@
|
|||
HOSTNAME=localhost
|
||||
CORS_HOSTNAME=http://localhost:8080
|
||||
RELEASE=stable
|
||||
NOTIFICATION=
|
||||
NOTIFICATION_TYPE=info
|
||||
SITE_NAME=Colubrina
|
||||
ALLOW_REGISTRATIONS=true
|
||||
PUBLIC_USERS=false
|
||||
EMAIL_VERIFICATION=false
|
||||
EMAIL_SMTP_HOST=smtp.myhost.com
|
||||
EMAIL_SMTP_PORT=587
|
||||
EMAIL_SMTP_USER=colubrina@myhost.com
|
||||
EMAIL_SMTP_FROM=colubrina@myhost.com
|
||||
EMAIL_SMTP_PASSWORD=myPassword
|
||||
EMAIL_SMTP_SECURE=false
|
||||
.ENV is no longer in use, replaced with:
|
||||
- config/database.json - Database credentials
|
||||
- config/config.json - Colubrina configuration (used to be .env)
|
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
|
@ -61,6 +61,7 @@ yarn-error.log*
|
|||
|
||||
# Config folder
|
||||
config/config.json
|
||||
config/database.json
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
|
|
5
backend/.sequelizerc
Normal file
5
backend/.sequelizerc
Normal file
|
@ -0,0 +1,5 @@
|
|||
var path = require('path')
|
||||
|
||||
module.exports = {
|
||||
'config': path.resolve('config/database.json'),
|
||||
}
|
|
@ -1,23 +1,18 @@
|
|||
{
|
||||
"development": {
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"database": "database",
|
||||
"host": "host",
|
||||
"dialect": "mariadb"
|
||||
},
|
||||
"test": {
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"database": "database",
|
||||
"host": "host",
|
||||
"dialect": "mariadb"
|
||||
},
|
||||
"production": {
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"database": "database",
|
||||
"host": "host",
|
||||
"dialect": "mariadb"
|
||||
}
|
||||
"hostname": "localhost",
|
||||
"corsHostname": "http://localhost:8080",
|
||||
"siteName": "Colubrina",
|
||||
"allowRegistrations": true,
|
||||
"notification": "",
|
||||
"notificationType": "",
|
||||
"release": "stable",
|
||||
"publicUsers": true,
|
||||
"emailVerification": false,
|
||||
"emailSMTPHost": "mail.example.com",
|
||||
"emailSMTPPort": 587,
|
||||
"emailSMTPUser": "colubrina@example.com",
|
||||
"emailSMTPFrom": "colubrina@example.com",
|
||||
"emailSMTPPassword": "",
|
||||
"emailSMTPSecure": false,
|
||||
"rules": "Write your instance rules here."
|
||||
}
|
||||
|
|
23
backend/config/database.example.json
Normal file
23
backend/config/database.example.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"development": {
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"database": "database",
|
||||
"host": "host",
|
||||
"dialect": "mariadb"
|
||||
},
|
||||
"test": {
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"database": "database",
|
||||
"host": "host",
|
||||
"dialect": "mariadb"
|
||||
},
|
||||
"production": {
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"database": "database",
|
||||
"host": "host",
|
||||
"dialect": "mariadb"
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ let app = express()
|
|||
let bodyParser = require("body-parser")
|
||||
let os = require("os")
|
||||
app.set("trust proxy", true)
|
||||
app.locals.config = require("./config/config.json")
|
||||
const socket = require("./lib/socket")
|
||||
const server = require("http").createServer(app)
|
||||
|
||||
|
@ -22,15 +23,17 @@ app.use("/api/v1/mediaproxy", require("./routes/mediaproxy.js"))
|
|||
app.use("/api/v1/associations", require("./routes/associations.js"))
|
||||
app.get("/api/v1/state", async (req, res) => {
|
||||
res.json({
|
||||
release: process.env.RELEASE,
|
||||
loading: true,
|
||||
notification: process.env.NOTIFICATION,
|
||||
notificationType: process.env.NOTIFICATION_TYPE,
|
||||
release: req.app.locals.config.release,
|
||||
notification: req.app.locals.config.notification,
|
||||
notificationType: req.app.locals.config.notificationType,
|
||||
latestVersion: require("../frontend/package.json").version,
|
||||
name: process.env.SITE_NAME,
|
||||
allowRegistrations: process.env.ALLOW_REGISTRATIONS === "true",
|
||||
publicUsers: process.env.PUBLIC_USERS === "true",
|
||||
emailVerification: process.env.EMAIL_VERIFICATION === "true"
|
||||
allowRegistrations: req.app.locals.config.allowRegistrations,
|
||||
publicUsers: req.app.locals.config.publicUsers,
|
||||
emailVerification: req.app.locals.config.emailVerification,
|
||||
rules: req.app.locals.config.rules,
|
||||
name: req.app.locals.config.siteName,
|
||||
loading: true,
|
||||
isColubrinaServer: true
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ module.exports = {
|
|||
init(app, server) {
|
||||
const io = require("socket.io")(server, {
|
||||
cors: {
|
||||
origin: [process.env.CORS_HOSTNAME],
|
||||
origin: [app.locals.config.corsHostname],
|
||||
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"]
|
||||
}
|
||||
})
|
||||
|
@ -19,10 +19,15 @@ module.exports = {
|
|||
console.log(socket.user.id)
|
||||
socket.join(user.id)
|
||||
socket.emit("siteState", {
|
||||
release: process.env.RELEASE,
|
||||
notification: process.env.NOTIFICATION,
|
||||
notificationType: process.env.NOTIFICATION_TYPE,
|
||||
latestVersion: require("../../frontend/package.json").version
|
||||
release: app.locals.config.release,
|
||||
notification: app.locals.config.notification,
|
||||
notificationType: app.locals.config.notificationType,
|
||||
latestVersion: require("../../frontend/package.json").version,
|
||||
allowRegistrations: app.locals.config.allowRegistrations,
|
||||
publicUsers: app.locals.config.publicUsers,
|
||||
emailVerification: app.locals.config.emailVerification,
|
||||
rules: app.locals.config.rules,
|
||||
name: app.locals.config.siteName
|
||||
})
|
||||
const friends = await Friend.findAll({
|
||||
where: {
|
||||
|
@ -114,10 +119,15 @@ module.exports = {
|
|||
} else {
|
||||
socket.join(-1)
|
||||
socket.emit("siteState", {
|
||||
release: process.env.RELEASE,
|
||||
notification: process.env.NOTIFICATION,
|
||||
notificationType: process.env.NOTIFICATION_TYPE,
|
||||
latestVersion: require("../../frontend/package.json").version
|
||||
release: app.locals.config.release,
|
||||
notification: app.locals.config.notification,
|
||||
notificationType: app.locals.config.notificationType,
|
||||
latestVersion: require("../../frontend/package.json").version,
|
||||
allowRegistrations: app.locals.config.allowRegistrations,
|
||||
publicUsers: app.locals.config.publicUsers,
|
||||
emailVerification: app.locals.config.emailVerification,
|
||||
rules: app.locals.config.rules,
|
||||
name: app.locals.config.siteName
|
||||
})
|
||||
socket.emit("unauthorized", {
|
||||
message: "Please reauth."
|
||||
|
|
|
@ -5,7 +5,7 @@ const path = require("path")
|
|||
const Sequelize = require("sequelize")
|
||||
const basename = path.basename(__filename)
|
||||
const env = process.env.NODE_ENV || "development"
|
||||
const config = require(__dirname + "/../config/config.json")[env]
|
||||
const config = require(__dirname + "/../config/database.json")[env]
|
||||
const db = {}
|
||||
|
||||
let sequelize
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"license": "GPL-3.0",
|
||||
"scripts": {
|
||||
"start": "node .",
|
||||
"serve": "nodemon"
|
||||
"serve": "nodemon . --ignore config"
|
||||
},
|
||||
"dependencies": {
|
||||
"argon2": "^0.28.7",
|
||||
|
|
|
@ -7,6 +7,7 @@ const { Op } = require("sequelize")
|
|||
const dayjs = require("dayjs")
|
||||
const fs = require("fs")
|
||||
const os = require("os")
|
||||
const argon2 = require("argon2")
|
||||
|
||||
router.all("*", auth, async (req, res, next) => {
|
||||
try {
|
||||
|
@ -22,7 +23,7 @@ router.all("*", auth, async (req, res, next) => {
|
|||
|
||||
router.all("*", auth, async (req, res, next) => {
|
||||
try {
|
||||
if (!req.user.emailVerified && process.env.EMAIL_VERIFICATION === "true") {
|
||||
if (!req.user.emailVerified && req.app.locals.config.emailVerification) {
|
||||
throw Errors.emailVerificationRequired
|
||||
} else {
|
||||
next()
|
||||
|
@ -53,8 +54,45 @@ router.get("/", auth, async (req, res, next) => {
|
|||
}
|
||||
})
|
||||
})
|
||||
} catch (err) {
|
||||
return next(err)
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
})
|
||||
|
||||
router.put("/user/:id", auth, async (req, res, next) => {
|
||||
try {
|
||||
const user = await User.findOne({
|
||||
where: {
|
||||
id: req.params.id
|
||||
}
|
||||
})
|
||||
if (!user) {
|
||||
throw Errors.communicationsUserNotFound
|
||||
} else {
|
||||
await user.update({
|
||||
banned: req.body.banned
|
||||
})
|
||||
res.json(user)
|
||||
}
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
})
|
||||
|
||||
router.post("/user", auth, async (req, res, next) => {
|
||||
try {
|
||||
const user = await User.create({
|
||||
username: req.body.username,
|
||||
password: await argon2.hash(req.body.password),
|
||||
email: req.body.email,
|
||||
emailVerified: req.body.emailVerified,
|
||||
admin: false,
|
||||
banned: false,
|
||||
lastSeenAt: new Date()
|
||||
})
|
||||
res.json(user)
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -231,19 +269,23 @@ router.put("/state", auth, async (req, res, next) => {
|
|||
}
|
||||
try {
|
||||
const io = req.app.get("io")
|
||||
setEnvValue("ALLOW_REGISTRATIONS", req.body.allowRegistrations)
|
||||
req.app.locals.config.allowRegistrations = req.body.allowRegistrations
|
||||
req.app.locals.config.rules = req.body.rules
|
||||
if (req.body.broadcastType === "permanent") {
|
||||
setEnvValue("NOTIFICATION", req.body.notification)
|
||||
setEnvValue("NOTIFICATION_TYPE", req.body.notificationType)
|
||||
process.env.NOTIFICATION = req.body.notification
|
||||
process.env.NOTIFICATION_TYPE = req.body.notificationType
|
||||
req.app.locals.config.notification = req.body.notification
|
||||
req.app.locals.config.notificationType = req.body.notificationType
|
||||
}
|
||||
io.emit("siteState", {
|
||||
notification: req.body.notification,
|
||||
notificationType: req.body.notificationType,
|
||||
latestVersion: require("../../frontend/package.json").version,
|
||||
allowRegistrations: req.body.allowRegistrations
|
||||
allowRegistrations: req.body.allowRegistrations,
|
||||
rules: req.body.rules
|
||||
})
|
||||
fs.writeFileSync(
|
||||
"./config/config.json",
|
||||
JSON.stringify(req.app.locals.config, null, 2)
|
||||
)
|
||||
res.sendStatus(204)
|
||||
} catch (err) {
|
||||
return next(err)
|
||||
|
|
|
@ -13,7 +13,7 @@ const {
|
|||
|
||||
router.all("*", auth, async (req, res, next) => {
|
||||
try {
|
||||
if (!req.user.emailVerified && process.env.EMAIL_VERIFICATION === "true") {
|
||||
if (!req.user.emailVerified && req.app.locals.config.emailVerification) {
|
||||
throw Errors.emailVerificationRequired
|
||||
} else {
|
||||
next()
|
||||
|
|
|
@ -155,7 +155,7 @@ async function createMessage(req, type, content, association, userId) {
|
|||
|
||||
router.all("*", auth, async (req, res, next) => {
|
||||
try {
|
||||
if (!req.user.emailVerified && process.env.EMAIL_VERIFICATION === "true") {
|
||||
if (!req.user.emailVerified && req.app.locals.config.emailVerification) {
|
||||
throw Errors.emailVerificationRequired
|
||||
} else {
|
||||
next()
|
||||
|
@ -376,7 +376,7 @@ router.get("/mutual/:id/groups", auth, async (req, res, next) => {
|
|||
|
||||
router.get("/users", auth, async (req, res, next) => {
|
||||
try {
|
||||
if (process.env.PUBLIC_USERS === "true") {
|
||||
if (req.app.locals.config.publicUsers) {
|
||||
const users = await User.findAll({
|
||||
attributes: [
|
||||
"id",
|
||||
|
|
|
@ -6,7 +6,7 @@ const router = express.Router()
|
|||
|
||||
router.all("*", auth, async (req, res, next) => {
|
||||
try {
|
||||
if (!req.user.emailVerified && process.env.EMAIL_VERIFICATION === "true") {
|
||||
if (!req.user.emailVerified && req.app.locals.config.emailVerification) {
|
||||
throw Errors.emailVerificationRequired
|
||||
} else {
|
||||
next()
|
||||
|
|
|
@ -72,7 +72,7 @@ const upload = multer({
|
|||
|
||||
router.post("/verify/resend", auth, mailLimiter, async (req, res, next) => {
|
||||
try {
|
||||
if (process.env.EMAIL_VERIFICATION !== "true") {
|
||||
if (!req.app.locals.config.emailVerification) {
|
||||
throw Errors.invalidParameter("Email verification is disabled")
|
||||
}
|
||||
if (req.user.emailVerified) {
|
||||
|
@ -85,20 +85,20 @@ router.post("/verify/resend", auth, mailLimiter, async (req, res, next) => {
|
|||
const mailGenerator = new Mailgen({
|
||||
theme: "default",
|
||||
product: {
|
||||
name: process.env.SITE_NAME,
|
||||
link: process.env.CORS_HOSTNAME
|
||||
name: req.app.locals.config.siteName,
|
||||
link: req.app.locals.config.corsHostname
|
||||
}
|
||||
})
|
||||
const email = {
|
||||
body: {
|
||||
name: req.user.username,
|
||||
intro: `${process.env.SITE_NAME} Account Verification`,
|
||||
intro: `${req.app.locals.config.siteName} Account Verification`,
|
||||
action: {
|
||||
instructions: `You are receiving this email because you registered on ${process.env.SITE_NAME}, please use the link below to verify your account.`,
|
||||
instructions: `You are receiving this email because you registered on ${req.app.locals.config.siteName}, please use the link below to verify your account.`,
|
||||
button: {
|
||||
color: "#1A97FF",
|
||||
text: "Account Verification",
|
||||
link: process.env.CORS_HOSTNAME + "/email/confirm/" + token
|
||||
link: req.app.locals.config.corsHostname + "/email/confirm/" + token
|
||||
}
|
||||
},
|
||||
outro: "If you did not register, please disregard this email."
|
||||
|
@ -108,18 +108,18 @@ router.post("/verify/resend", auth, mailLimiter, async (req, res, next) => {
|
|||
|
||||
const emailText = mailGenerator.generatePlaintext(email)
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: process.env.EMAIL_SMTP_HOST,
|
||||
port: process.env.EMAIL_SMTP_PORT,
|
||||
secure: JSON.parse(process.env.EMAIL_SMTP_SECURE.toLowerCase()),
|
||||
host: req.app.locals.config.emailSMTPHost,
|
||||
port: req.app.locals.config.emailSMTPPort,
|
||||
secure: req.app.locals.config.emailSMTPSecure,
|
||||
auth: {
|
||||
user: process.env.EMAIL_SMTP_USER,
|
||||
pass: process.env.EMAIL_SMTP_PASSWORD
|
||||
user: req.app.locals.config.emailSMTPUser,
|
||||
pass: req.app.locals.config.emailSMTPPassword
|
||||
}
|
||||
})
|
||||
let info = await transporter.sendMail({
|
||||
from: process.env.EMAIL_SMTP_FROM,
|
||||
from: req.app.locals.config.emailSMTPFrom,
|
||||
to: req.user.email,
|
||||
subject: "Email Verification - " + process.env.SITE_NAME,
|
||||
subject: "Email Verification - " + req.app.locals.config.siteName,
|
||||
text: emailText,
|
||||
html: emailBody
|
||||
})
|
||||
|
@ -135,7 +135,7 @@ router.post("/verify/resend", auth, mailLimiter, async (req, res, next) => {
|
|||
|
||||
router.post("/verify/confirm/:token", async (req, res, next) => {
|
||||
try {
|
||||
if (process.env.EMAIL_VERIFICATION !== "true") {
|
||||
if (!req.app.locals.config.emailVerification) {
|
||||
throw Errors.invalidParameter("Email verification is disabled")
|
||||
}
|
||||
if (!req.params.token) {
|
||||
|
@ -294,7 +294,7 @@ router.post("/register", limiter, async (req, res, next) => {
|
|||
}
|
||||
}
|
||||
try {
|
||||
if (process.env.ALLOW_REGISTRATIONS !== "true") {
|
||||
if (!req.app.locals.config.allowRegistrations) {
|
||||
throw Errors.registrationsDisabled
|
||||
}
|
||||
if (req.body.password.length < 8) {
|
||||
|
|
189
cli/index.js
189
cli/index.js
|
@ -9,6 +9,11 @@ const os = require("os")
|
|||
const { execSync } = require("child_process")
|
||||
|
||||
console.log("Troplo/Colubrina CLI")
|
||||
if (fs.existsSync("../backend/config/config.json")) {
|
||||
console.log(
|
||||
"Want to modify either the Colubrina, or database config? Check out the config files in backend/config."
|
||||
)
|
||||
}
|
||||
console.log("Colubrina version", require("../frontend/package.json").version)
|
||||
async function checkForUpdates() {
|
||||
if (!process.argv.includes("--skip-update")) {
|
||||
|
@ -139,10 +144,10 @@ async function testDB() {
|
|||
async function dbSetup() {
|
||||
await doSetupDB()
|
||||
fs.writeFileSync(
|
||||
path.join(__dirname, "../backend/config/config.json"),
|
||||
JSON.stringify(state.dbConfig)
|
||||
path.join(__dirname, "../backend/config/database.json"),
|
||||
JSON.stringify(state.dbConfig, null, 2)
|
||||
)
|
||||
console.log("config/config.json overwritten")
|
||||
console.log("config/database.json overwritten")
|
||||
}
|
||||
async function runMigrations() {
|
||||
console.log("Running migrations")
|
||||
|
@ -171,116 +176,61 @@ async function createUser() {
|
|||
console.log("User created")
|
||||
}
|
||||
async function configureDotEnv() {
|
||||
function setEnvValue(key, value) {
|
||||
const ENV_VARS = fs.readFileSync("../backend/.env", "utf8").split(os.EOL)
|
||||
|
||||
// find the env we want based on the key
|
||||
const target = ENV_VARS.indexOf(
|
||||
ENV_VARS.find((line) => {
|
||||
// (?<!#\s*) Negative lookbehind to avoid matching comments (lines that starts with #).
|
||||
// There is a double slash in the RegExp constructor to escape it.
|
||||
// (?==) Positive lookahead to check if there is an equal sign right after the key.
|
||||
// This is to prevent matching keys prefixed with the key of the env var to update.
|
||||
const keyValRegex = new RegExp(`(?<!#\\s*)${key}(?==)`)
|
||||
|
||||
return line.match(keyValRegex)
|
||||
})
|
||||
)
|
||||
|
||||
// if key-value pair exists in the .env file,
|
||||
if (target !== -1) {
|
||||
// replace the key/value with the new value
|
||||
ENV_VARS.splice(target, 1, `${key}=${value}`)
|
||||
} else {
|
||||
// if it doesn't exist, add it instead
|
||||
ENV_VARS.push(`${key}=${value}`)
|
||||
}
|
||||
|
||||
// write everything back to the file system
|
||||
fs.writeFileSync("../backend/.env", ENV_VARS.join(os.EOL))
|
||||
if (!fs.existsSync("../backend/config/config.json")) {
|
||||
fs.writeFileSync("../backend/config/config.json", "{}")
|
||||
}
|
||||
if (!fs.existsSync("../backend/.env")) {
|
||||
fs.writeFileSync("../backend/.env", "")
|
||||
}
|
||||
setEnvValue(
|
||||
"HOSTNAME",
|
||||
await input.text("Public Domain", {
|
||||
default: "localhost"
|
||||
})
|
||||
)
|
||||
setEnvValue(
|
||||
"CORS_HOSTNAME",
|
||||
await input.text("Public Hostname", {
|
||||
default: "http://localhost:8080"
|
||||
})
|
||||
)
|
||||
setEnvValue(
|
||||
"SITE_NAME",
|
||||
await input.text("Site Name", {
|
||||
default: "Colubrina"
|
||||
})
|
||||
)
|
||||
setEnvValue(
|
||||
"ALLOW_REGISTRATIONS",
|
||||
await input.text("Permit Public Registrations", {
|
||||
default: false
|
||||
})
|
||||
)
|
||||
setEnvValue(
|
||||
"PUBLIC_USERS",
|
||||
await input.text("Show instance users publicly", {
|
||||
default: false
|
||||
})
|
||||
)
|
||||
const emailVerify = await input.text("Enforce email verification?", {
|
||||
let config = require("../backend/config/config.json")
|
||||
config.hostname = await input.text("Public Domain", {
|
||||
default: "localhost"
|
||||
})
|
||||
config.corsHostname = await input.text("CORS Hostname", {
|
||||
default: "http://localhost"
|
||||
})
|
||||
config.siteName = await input.text("Site Name", {
|
||||
default: "Colubrina"
|
||||
})
|
||||
config.allowRegistrations = await input.text("Allow Registrations", {
|
||||
default: true
|
||||
})
|
||||
config.publicUsers = await input.text("Show instance users publicly?", {
|
||||
default: true
|
||||
})
|
||||
config.emailVerify = await input.text("Enforce email verification?", {
|
||||
default: false
|
||||
})
|
||||
if (emailVerify) {
|
||||
setEnvValue("EMAIL_VERIFICATION", true)
|
||||
setEnvValue(
|
||||
"EMAIL_SMTP_HOST",
|
||||
await input.text("SMTP Host", {
|
||||
default: "smtp.myhost.com"
|
||||
})
|
||||
)
|
||||
setEnvValue(
|
||||
"EMAIL_SMTP_PORT",
|
||||
await input.text("SMTP Port", {
|
||||
default: 587
|
||||
})
|
||||
)
|
||||
setEnvValue(
|
||||
"EMAIL_SMTP_USER",
|
||||
await input.text("SMTP User", {
|
||||
default: "colubrina@myhost.com"
|
||||
})
|
||||
)
|
||||
setEnvValue(
|
||||
"EMAIL_SMTP_FROM",
|
||||
await input.text("SMTP From Address", {
|
||||
default: "colubrina@myhost.com"
|
||||
})
|
||||
)
|
||||
setEnvValue("EMAIL_SMTP_PASSWORD", await input.text("SMTP Password", {}))
|
||||
setEnvValue(
|
||||
"EMAIL_SMTP_SECURE",
|
||||
await input.text("SMTP Secure", {
|
||||
default: false
|
||||
})
|
||||
)
|
||||
if (config.emailVerify) {
|
||||
config.emailSMTPHost = await input.text("SMTP Host", {
|
||||
default: "mail.example.com"
|
||||
})
|
||||
config.emailSMTPPort = await input.text("SMTP Port", {
|
||||
default: 587
|
||||
})
|
||||
config.emailSMTPUsername = await input.text("SMTP Username", {
|
||||
default: "colubrina@example.com"
|
||||
})
|
||||
config.emailSMTPPassword = await input.text("SMTP Password", {})
|
||||
config.emailSMTPFrom = await input.text("SMTP From Address", {
|
||||
default: "colubrina@example.com"
|
||||
})
|
||||
config.emailSMTPSecure = await input.text("SMTP Secure", {
|
||||
default: true
|
||||
})
|
||||
} else {
|
||||
setEnvValue("EMAIL_VERIFICATION", false)
|
||||
setEnvValue("EMAIL_SMTP_HOST", "smtp.myhost.com")
|
||||
setEnvValue("EMAIL_SMTP_PORT", "587")
|
||||
setEnvValue("EMAIL_SMTP_USER", "colubrina@myhost.com")
|
||||
setEnvValue("EMAIL_SMTP_FROM", "colubrina@myhost.com")
|
||||
setEnvValue("EMAIL_SMTP_PASSWORD", "myPassword")
|
||||
setEnvValue("EMAIL_SMTP_SECURE", false)
|
||||
config.emailSMTPHost = "smtp.myhost.com"
|
||||
config.emailSMTPPort = 587
|
||||
config.emailSMTPUsername = "colubrina@example.com"
|
||||
config.emailSMTPFrom = "colubrina@example.com"
|
||||
config.emailSMTPPassword = ""
|
||||
config.emailSMTPSecure = true
|
||||
}
|
||||
setEnvValue("PUBLIC_USERS")
|
||||
setEnvValue("NOTIFICATION", "")
|
||||
setEnvValue("NOTIFICATION_TYPE", "info")
|
||||
setEnvValue("RELEASE", "stable")
|
||||
config.notification = ""
|
||||
config.notificationType = "info"
|
||||
config.release = "stable"
|
||||
config.rules = "Write your instance rules here."
|
||||
fs.writeFileSync(
|
||||
"../backend/config/config.json",
|
||||
JSON.stringify(config, null, 2)
|
||||
)
|
||||
}
|
||||
async function init() {
|
||||
while (true) {
|
||||
|
@ -288,7 +238,7 @@ async function init() {
|
|||
"First-time setup",
|
||||
"Create user",
|
||||
"Run migrations",
|
||||
"Update/create config file",
|
||||
"Update/create database config file",
|
||||
"Check for updates",
|
||||
"Build frontend for production",
|
||||
"Exit"
|
||||
|
@ -303,10 +253,15 @@ async function init() {
|
|||
execSync("cd ../frontend && yarn install --frozen-lockfile", () => {
|
||||
console.log("yarn install complete (frontend)")
|
||||
})
|
||||
if (fs.existsSync(path.join(__dirname, "../backend/.env"))) {
|
||||
const option = await input.confirm(".env already exists, overwrite?", {
|
||||
default: false
|
||||
})
|
||||
if (
|
||||
fs.existsSync(path.join(__dirname, "../backend/config/config.json"))
|
||||
) {
|
||||
const option = await input.confirm(
|
||||
"Colubrina config already exists, overwrite?",
|
||||
{
|
||||
default: false
|
||||
}
|
||||
)
|
||||
if (option) {
|
||||
await configureDotEnv()
|
||||
}
|
||||
|
@ -314,10 +269,10 @@ async function init() {
|
|||
await configureDotEnv()
|
||||
}
|
||||
if (
|
||||
fs.existsSync(path.join(__dirname, "../backend/config/config.json"))
|
||||
fs.existsSync(path.join(__dirname, "../backend/config/database.json"))
|
||||
) {
|
||||
const option = await input.select(
|
||||
`config/config.json already exists. Do you want to overwrite it?`,
|
||||
`config/database.json already exists. Do you want to overwrite it?`,
|
||||
["Yes", "No"]
|
||||
)
|
||||
if (option === "Yes") {
|
||||
|
@ -368,9 +323,9 @@ async function init() {
|
|||
console.log(
|
||||
"The Colubrina frontend can be built with `yarn build` in the root project directory, and is recommended to be served via NGINX, with a proxy_pass to the backend on /api and /socket.io."
|
||||
)
|
||||
} else if (option === "Update/create config file") {
|
||||
} else if (option === "Update/create database config file") {
|
||||
await dbSetup()
|
||||
console.log("config/config.json overwritten or created")
|
||||
console.log("config/database.json overwritten or created")
|
||||
} else if (option === "Create user") {
|
||||
await createUser()
|
||||
} else if (option === "Run migrations") {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "colubrina",
|
||||
"version": "1.0.18",
|
||||
"version": "1.0.19",
|
||||
"private": true,
|
||||
"author": "Troplo <troplo@troplo.com>",
|
||||
"scripts": {
|
||||
|
|
|
@ -624,6 +624,9 @@ export default {
|
|||
this.$store.state.site.latestVersion = state.latestVersion
|
||||
this.$store.state.site.notification = state.notification
|
||||
this.$store.state.site.notificationType = state.notificationType
|
||||
this.$store.state.site.allowRegistrations = state.allowRegistrations
|
||||
this.$store.state.site.rules = state.rules
|
||||
this.$store.state.site.emailVerification = state.emailVerification
|
||||
})
|
||||
// eslint-disable-next-line no-undef
|
||||
this.$store.dispatch("updateQuickSwitch")
|
||||
|
|
|
@ -32,6 +32,14 @@
|
|||
label="Allow registrations"
|
||||
v-model="allowRegistrations"
|
||||
></v-switch>
|
||||
<v-textarea ref="rules" label="Instance Rules" v-model="rules" class="mx-3">
|
||||
</v-textarea>
|
||||
<v-card-title>
|
||||
<v-icon class="mr-1">mdi-language-markdown</v-icon>Rules Preview:
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<span v-markdown class="mx-3" :key="rules">{{ rules }}</span>
|
||||
</v-card-text>
|
||||
<v-btn text class="mx-3 mb-3" color="primary" @click="updateState"
|
||||
>Save</v-btn
|
||||
>
|
||||
|
@ -47,6 +55,7 @@ export default {
|
|||
notification: "",
|
||||
notificationType: "info",
|
||||
allowRegistrations: true,
|
||||
rules: "",
|
||||
notificationTypes: [
|
||||
{ text: "Info", value: "info" },
|
||||
{ text: "Success", value: "success" },
|
||||
|
@ -73,7 +82,8 @@ export default {
|
|||
notification: this.notification,
|
||||
notificationType: this.notificationType,
|
||||
broadcastType: this.broadcastType,
|
||||
allowRegistrations: this.allowRegistrations
|
||||
allowRegistrations: this.allowRegistrations,
|
||||
rules: this.rules
|
||||
})
|
||||
.then(() => {
|
||||
this.$toast.success("State updated")
|
||||
|
@ -87,6 +97,7 @@ export default {
|
|||
this.notification = this.$store.state.site.notification
|
||||
this.notificationType = this.$store.state.site.notificationType
|
||||
this.allowRegistrations = this.$store.state.site.allowRegistrations
|
||||
this.rules = this.$store.state.site.rules
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,8 +1,38 @@
|
|||
<template>
|
||||
<div id="admin-users">
|
||||
<v-dialog width="500" v-model="create.dialog">
|
||||
<v-card color="card">
|
||||
<v-toolbar color="toolbar">
|
||||
<v-toolbar-title>Create User</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<v-container>
|
||||
<v-form @submit.prevent="createUser">
|
||||
<v-text-field
|
||||
label="Username"
|
||||
v-model="create.username"
|
||||
></v-text-field>
|
||||
<v-text-field label="Email" v-model="create.email"></v-text-field>
|
||||
<v-text-field
|
||||
label="Password"
|
||||
type="password"
|
||||
v-model="create.password"
|
||||
></v-text-field>
|
||||
<v-switch
|
||||
inset
|
||||
label="Email Verified?"
|
||||
v-model="create.emailVerified"
|
||||
></v-switch>
|
||||
<v-btn text type="submit" color="primary">Create</v-btn>
|
||||
</v-form>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-toolbar color="toolbar">
|
||||
<v-toolbar-title>Users ({{ users.count }})</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn @click="create.dialog = true" icon>
|
||||
<v-icon>mdi-plus</v-icon>
|
||||
</v-btn>
|
||||
<v-btn @click="getUsers" icon>
|
||||
<v-icon>mdi-refresh</v-icon>
|
||||
</v-btn>
|
||||
|
@ -19,6 +49,21 @@
|
|||
<template v-slot:item.index="{ index }">
|
||||
{{ index }}
|
||||
</template>
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-btn
|
||||
v-on="on"
|
||||
icon
|
||||
@click="banUser(item)"
|
||||
:disabled="item.id === $store.state.user.id || item.admin"
|
||||
>
|
||||
<v-icon>mdi-gavel</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<span>Ban</span>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -31,6 +76,13 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
users: [],
|
||||
create: {
|
||||
dialog: false,
|
||||
username: "",
|
||||
email: "",
|
||||
password: "",
|
||||
emailVerified: false
|
||||
},
|
||||
headers: [
|
||||
{
|
||||
text: "Index",
|
||||
|
@ -68,11 +120,59 @@ export default {
|
|||
{
|
||||
text: "Last Seen At",
|
||||
value: "lastSeenAt"
|
||||
},
|
||||
{
|
||||
text: "Admin",
|
||||
value: "admin"
|
||||
},
|
||||
{
|
||||
text: "Banned",
|
||||
value: "banned"
|
||||
},
|
||||
{
|
||||
text: "Email Verified",
|
||||
value: "emailVerified"
|
||||
},
|
||||
{
|
||||
text: "Actions",
|
||||
value: "actions",
|
||||
sortable: false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
createUser() {
|
||||
this.axios
|
||||
.post(`/api/v1/admin/user`, this.create)
|
||||
.then(() => {
|
||||
this.getUsers()
|
||||
this.$toast.success("Action performed successfully.")
|
||||
this.create = {
|
||||
dialog: false,
|
||||
username: "",
|
||||
email: "",
|
||||
password: "",
|
||||
emailVerified: false
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
})
|
||||
},
|
||||
banUser(item) {
|
||||
this.axios
|
||||
.put(`/api/v1/admin/user/${item.id}`, {
|
||||
banned: !item.banned
|
||||
})
|
||||
.then(() => {
|
||||
this.getUsers()
|
||||
this.$toast.success("Action performed successfully.")
|
||||
})
|
||||
.catch((e) => {
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
})
|
||||
},
|
||||
getUsers() {
|
||||
this.axios
|
||||
.get("/api/v1/admin/users")
|
||||
|
|
|
@ -30,8 +30,7 @@
|
|||
<v-form ref="form" class="pa-4 pt-6">
|
||||
<p class="text-center text-h4">
|
||||
Login to
|
||||
<span class="troplo-title">{{ $store.state.site.name }}</span
|
||||
><small style="font-size: 15px">beta</small>
|
||||
<span class="troplo-title">{{ $store.state.site.name }}</span>
|
||||
</p>
|
||||
<v-text-field
|
||||
@keyup.enter="doLogin()"
|
||||
|
|
|
@ -1,5 +1,19 @@
|
|||
<template>
|
||||
<div id="login" v-if="!$store.state.user?.bcUser?.id">
|
||||
<div id="login" v-if="!$store.state.user?.id">
|
||||
<v-dialog v-model="rulesDialog" max-width="700px">
|
||||
<v-card color="card">
|
||||
<v-toolbar color="toolbar">
|
||||
<v-toolbar-title>
|
||||
{{ $store.state.site.name }} Rules
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<v-card-text class="mt-3" style="color: unset">
|
||||
<span v-markdown :key="$store.state.site.rules">{{
|
||||
$store.state.site.rules
|
||||
}}</span>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<div :class="{ outer: !$vuetify.breakpoint.mobile }">
|
||||
<div :class="{ middle: !$vuetify.breakpoint.mobile }">
|
||||
<div :class="{ innerLogin: !$vuetify.breakpoint.mobile }">
|
||||
|
@ -8,8 +22,7 @@
|
|||
<v-form ref="form" class="pa-4 pt-6">
|
||||
<p class="text-center text-h4">
|
||||
Register to
|
||||
<span class="troplo-title">{{ $store.state.site.name }}</span
|
||||
><small style="font-size: 15px">beta</small>
|
||||
<span class="troplo-title">{{ $store.state.site.name }}</span>
|
||||
</p>
|
||||
<v-text-field
|
||||
@keyup.enter="doRegister()"
|
||||
|
@ -52,6 +65,38 @@
|
|||
>This instance has email verification enforced, ensure your
|
||||
email is correct.</small
|
||||
>
|
||||
<v-row align="center">
|
||||
<v-tooltip top v-if="!rulesOpenedOnce">
|
||||
<template v-slot:activator="{ on }">
|
||||
<div v-on="on">
|
||||
<v-switch
|
||||
class="ml-4 mt-5"
|
||||
inset
|
||||
v-model="rules"
|
||||
:disabled="!rulesOpenedOnce"
|
||||
></v-switch>
|
||||
</div>
|
||||
</template>
|
||||
<span>You need to view the rules first.</span>
|
||||
</v-tooltip>
|
||||
<v-switch
|
||||
class="ml-4 mt-5"
|
||||
inset
|
||||
v-model="rules"
|
||||
v-else
|
||||
:disabled="!rulesOpenedOnce"
|
||||
></v-switch>
|
||||
I have read and agree to the
|
||||
<a
|
||||
@click="
|
||||
rulesDialog = true
|
||||
rulesOpenedOnce = true
|
||||
"
|
||||
target="_blank"
|
||||
>
|
||||
instance rules </a
|
||||
>.
|
||||
</v-row>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
|
@ -96,6 +141,9 @@ export default {
|
|||
totp: "",
|
||||
totpDialog: false,
|
||||
loading: false,
|
||||
rules: false,
|
||||
rulesDialog: false,
|
||||
rulesOpenedOnce: false,
|
||||
instance:
|
||||
localStorage.getItem("instance") || "https://colubrina.troplo.com",
|
||||
instanceString: ""
|
||||
|
@ -123,6 +171,12 @@ export default {
|
|||
return window.innerHeight
|
||||
},
|
||||
doRegister() {
|
||||
if (!this.rules) {
|
||||
this.$toast.error(
|
||||
"You need to accept the rules before you can register."
|
||||
)
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
this.axios
|
||||
.post("/api/v1/user/register", {
|
||||
|
|
|
@ -94,9 +94,37 @@ module.exports = {
|
|||
builderOptions: {
|
||||
appId: "com.troplo.colubrina",
|
||||
win: {
|
||||
target: [
|
||||
{
|
||||
target: "nsis",
|
||||
arch: ["x64", "ia32", "armv7l", "arm64"]
|
||||
}
|
||||
],
|
||||
publish: ["github"]
|
||||
},
|
||||
linux: {
|
||||
target: [
|
||||
{
|
||||
target: "AppImage",
|
||||
arch: ["x64", "ia32", "armv7l", "arm64"]
|
||||
},
|
||||
{
|
||||
target: "deb",
|
||||
arch: ["x64", "ia32", "armv7l", "arm64"]
|
||||
},
|
||||
{
|
||||
target: "snap",
|
||||
arch: ["x64", "ia32", "armv7l", "arm64"]
|
||||
},
|
||||
{
|
||||
target: "pacman",
|
||||
arch: ["x64", "ia32", "armv7l", "arm64"]
|
||||
},
|
||||
{
|
||||
target: "tar.gz",
|
||||
arch: ["x64", "ia32", "armv7l", "arm64"]
|
||||
}
|
||||
],
|
||||
publish: ["github"],
|
||||
category: "Network",
|
||||
synopsis: "Instant Messaging",
|
||||
|
|
Loading…
Reference in a new issue