srijeda, 11. novembar 2026. · Godina I · Broj 15 · Cijena: 0 dinara
JUGOSLOVENSKI FORUM

Glasilo YU mreže · NEMA GRANICA ZA ZNANJE

Prijava Upis
Pravo na uvid u kod. Otvoren protokol omogućava nezavisne operatere i interoperabilnost. Manifest

Izvor

4d7883a894b3

   1 import fs from 'node:fs';
   2 import path from 'node:path';
   3 import {GRADOVI} from './gradovi.js';
   4 export const SCHEMA2=`
   5 CREATE TABLE IF NOT EXISTS names(name TEXT PRIMARY KEY,skeleton TEXT,owner_id INTEGER,kind TEXT,net TEXT,origin TEXT,service_id TEXT,status TEXT,trust TEXT,sequence INTEGER,expires_at INTEGER,policy_version INTEGER,created_at INTEGER,updated_at INTEGER,sig TEXT,key_id TEXT);
   6 CREATE TABLE IF NOT EXISTS reg_keys(key_id TEXT PRIMARY KEY,public_key TEXT,created_at INTEGER,retired_at INTEGER);
   7 CREATE TABLE IF NOT EXISTS reg_log(seq INTEGER PRIMARY KEY,at INTEGER,action TEXT,name TEXT,actor_id INTEGER,payload TEXT,prev_hash TEXT,hash TEXT,sig TEXT,key_id TEXT);
   8 CREATE TABLE IF NOT EXISTS approvals(id INTEGER PRIMARY KEY,subject TEXT,subject_id TEXT,steward_id INTEGER,at INTEGER,UNIQUE(subject,subject_id,steward_id));
   9 CREATE TABLE IF NOT EXISTS projects(id INTEGER PRIMARY KEY,owner_id INTEGER,title TEXT,kind TEXT,summary TEXT,city_id TEXT,name TEXT,url TEXT,lang TEXT,created_at INTEGER);
  10 CREATE TABLE IF NOT EXISTS pages(name TEXT PRIMARY KEY,owner_id INTEGER,title TEXT,body TEXT,published INTEGER,updated_at INTEGER);
  11 CREATE TABLE IF NOT EXISTS follows(user_id INTEGER,kind TEXT,target_id TEXT,at INTEGER,PRIMARY KEY(user_id,kind,target_id));
  12 CREATE TABLE IF NOT EXISTS listings(id INTEGER PRIMARY KEY,seller_id INTEGER,title TEXT,body TEXT,kind TEXT,price_eur_text TEXT,price_dinar INTEGER,stock INTEGER,status TEXT,created_at INTEGER);
  13 CREATE TABLE IF NOT EXISTS orders(id INTEGER PRIMARY KEY,listing_id INTEGER,buyer_id INTEGER,seller_id INTEGER,amount INTEGER,status TEXT,created_at INTEGER,updated_at INTEGER,release_at INTEGER);
  14 CREATE TABLE IF NOT EXISTS ratings(order_id INTEGER,from_id INTEGER,to_id INTEGER,score INTEGER,note TEXT,at INTEGER,PRIMARY KEY(order_id,from_id));
  15 CREATE TABLE IF NOT EXISTS disputes(id INTEGER PRIMARY KEY,kind TEXT,ref TEXT,opener_id INTEGER,respondent_id INTEGER,statement TEXT,response TEXT,reviewer_id INTEGER,decision TEXT,status TEXT,deadline_at INTEGER,appeal_of INTEGER,redact INTEGER,created_at INTEGER,decided_at INTEGER);
  16 CREATE TABLE IF NOT EXISTS proposals(id INTEGER PRIMARY KEY,kind TEXT,title TEXT,body TEXT,options TEXT,author_id INTEGER,thread_id INTEGER,opens_at INTEGER,closes_at INTEGER,quorum INTEGER,threshold REAL,status TEXT,result TEXT);
  17 CREATE TABLE IF NOT EXISTS ballots(proposal_id INTEGER,user_id INTEGER,choice TEXT,at INTEGER,PRIMARY KEY(proposal_id,user_id));
  18 CREATE TABLE IF NOT EXISTS candidates(proposal_id INTEGER,user_id INTEGER,seat TEXT,statement TEXT,PRIMARY KEY(proposal_id,user_id));
  19 CREATE TABLE IF NOT EXISTS mandates(id INTEGER PRIMARY KEY,user_id INTEGER,seat TEXT,starts_at INTEGER,ends_at INTEGER,proposal_id INTEGER);
  20 CREATE TABLE IF NOT EXISTS mirrors(id INTEGER PRIMARY KEY,owner_id INTEGER,url TEXT,city_id TEXT,note TEXT,created_at INTEGER,simulated INTEGER);
  21 CREATE TABLE IF NOT EXISTS invites(code TEXT PRIMARY KEY,user_id INTEGER,created_at INTEGER,uses INTEGER DEFAULT 0);
  22 CREATE TABLE IF NOT EXISTS returns(handle TEXT PRIMARY KEY,code_hash TEXT,member_no INTEGER,payload TEXT,expires_at INTEGER);
  23 CREATE TABLE IF NOT EXISTS lesson_done(user_id INTEGER,lesson TEXT,at INTEGER,PRIMARY KEY(user_id,lesson));
  24 CREATE TABLE IF NOT EXISTS counters(key TEXT,day TEXT,n INTEGER,PRIMARY KEY(key,day));
  25 CREATE TABLE IF NOT EXISTS docs(slug TEXT PRIMARY KEY,title TEXT,body TEXT,version INTEGER,updated_at INTEGER);
  26 CREATE TABLE IF NOT EXISTS card_instances(id INTEGER PRIMARY KEY,edition TEXT,serial INTEGER,owner_id INTEGER,minted_at INTEGER,source TEXT,sig TEXT,key_id TEXT,UNIQUE(edition,serial));
  27 CREATE TABLE IF NOT EXISTS card_transfers(id INTEGER PRIMARY KEY,card_id INTEGER,from_id INTEGER,to_id INTEGER,kind TEXT,trade_id INTEGER,at INTEGER);
  28 CREATE TABLE IF NOT EXISTS trades(id INTEGER PRIMARY KEY,from_id INTEGER,to_id INTEGER,give_json TEXT,want_json TEXT,dinar INTEGER,status TEXT,created_at INTEGER,decided_at INTEGER);
  29 CREATE TABLE IF NOT EXISTS card_shares(token TEXT PRIMARY KEY,user_id INTEGER,fields_json TEXT,created_at INTEGER,revoked INTEGER DEFAULT 0);
  30 CREATE TABLE IF NOT EXISTS name_transfers(name TEXT PRIMARY KEY,from_id INTEGER,to_id INTEGER,expires_at INTEGER);
  31 CREATE TABLE IF NOT EXISTS work_submissions(id INTEGER PRIMARY KEY,user_id INTEGER,lesson INTEGER,lang TEXT,body TEXT,reviewer_id INTEGER,completed_at INTEGER,created_at INTEGER,UNIQUE(lesson,lang,user_id));
  32 CREATE TABLE IF NOT EXISTS activities(id INTEGER PRIMARY KEY,user_id INTEGER,kind TEXT,ref TEXT,at INTEGER);
  33 CREATE INDEX IF NOT EXISTS activity_date ON activities(at,user_id);
  34 `;
  35 export function migrate(db,seed) {
  36  if(Number(db.meta('schema_version'))>=2)return false;
  37  const hasV1=!!db.get('SELECT 1 FROM users LIMIT 1');
  38  if(hasV1){const dir=path.join(db.dataDir,'backups');fs.mkdirSync(dir,{recursive:true});const f=path.join(dir,'forum-v1-final.db');if(!fs.existsSync(f))db.raw.exec(`VACUUM INTO '${f.replaceAll("'","''")}'`);}
  39  db.raw.exec('PRAGMA foreign_keys=OFF');
  40  try{db.tx(()=>{
  41   db.raw.exec(SCHEMA2);
  42   for(const [table,col,type] of [['users','city_id','TEXT'],['users','pismo',"TEXT DEFAULT 'lat'"],['users','abuse_contact',"TEXT DEFAULT ''"],['users','invited_by','INTEGER'],['threads','lang',"TEXT DEFAULT 'sh'"],['posts','lang',"TEXT DEFAULT 'sh'"]]){
  43    if(!db.all(`PRAGMA table_info(${table})`).some(x=>x.name===col))db.raw.exec(`ALTER TABLE ${table} ADD COLUMN ${col} ${type}`);
  44   }
  45   let previousCategories=[];
  46   if(hasV1){
  47    previousCategories=db.all('SELECT t.id,c.slug FROM threads t JOIN categories c ON c.id=t.category_id');
  48    // Real contributions survive. The permanent backup also retains the retired demonstration conversations.
  49    db.run('DELETE FROM reactions WHERE post_id IN (SELECT id FROM posts WHERE user_id IN (SELECT id FROM users WHERE is_seed=1 OR is_test=1))');
  50    db.run('DELETE FROM posts WHERE user_id IN (SELECT id FROM users WHERE is_seed=1 OR is_test=1)');
  51    db.run('DELETE FROM threads WHERE id NOT IN (SELECT thread_id FROM posts)');
  52    db.run('DELETE FROM user_cards WHERE user_id IN (SELECT id FROM users WHERE is_seed=1 OR is_test=1 OR deleted=1)');
  53    for(const table of ['notifications','reports','search','thread_reads'])db.run(`DELETE FROM ${table}`);
  54    db.run('DELETE FROM sessions WHERE user_id IN (SELECT id FROM users WHERE is_seed=1 OR is_test=1 OR deleted=1)');
  55    db.run('DELETE FROM ledger WHERE user_id IN (SELECT id FROM users WHERE is_seed=1 OR is_test=1 OR deleted=1)');
  56    db.run('DELETE FROM users WHERE is_seed=1 OR is_test=1 OR deleted=1');
  57    db.run('DELETE FROM categories');
  58    for(const u of db.all('SELECT id,city FROM users'))db.run('UPDATE users SET city_id=?,city=? WHERE id=?',GRADOVI.find(g=>g.ime===u.city)?.id??null,'',u.id);
  59   }
  60   seed();
  61   if(hasV1){const admin=db.get("SELECT id FROM users WHERE handle='signal'").id,cat=db.get("SELECT id FROM categories WHERE slug='kafana'").id;
  62    db.run('UPDATE threads SET user_id=? WHERE user_id NOT IN (SELECT id FROM users)',admin);
  63    const renamed={'signal-desk':'oglasna-tabla',welcome:'prvi-koraci',registry:'registar',market:'pijaca',culture:'kultura',builders:'radionica',governance:'uprava'};
  64    for(const prior of previousCategories){const target=db.get('SELECT id FROM categories WHERE slug=?',renamed[prior.slug]||prior.slug)?.id||cat;db.run('UPDATE threads SET category_id=? WHERE id=?',target,prior.id);}
  65    db.run('UPDATE threads SET category_id=? WHERE category_id NOT IN (SELECT id FROM categories)',cat);
  66    db.run('INSERT INTO search(title,body,post_id,thread_id) SELECT t.title,p.body,p.id,t.id FROM posts p JOIN threads t ON t.id=p.thread_id WHERE p.id NOT IN (SELECT post_id FROM search)');
  67   }
  68   db.meta('schema_version',2);
  69  });}finally{db.raw.exec('PRAGMA foreign_keys=ON');}
  70  return true;
  71 }
  72